home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / reply.c < prev    next >
C/C++ Source or Header  |  1996-03-25  |  77KB  |  2,592 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: reply.c,v 4.172 1996/03/25 08:03:52 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.     reply.c
  44.    
  45.    Code here for forward and reply to mail
  46.    A few support routines as well
  47.  
  48.   This code will forward and reply to MIME messages. The Pine composer
  49. at this time will only support non-text segments at the end of a
  50. message so, things don't always come out as one would like. If you
  51. always forward a message in MIME format, all will be correct.  Forwarding
  52. of nested MULTIPART messages will work.  There's still a problem with
  53. MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying
  54. the equivalent parts.  Ideally, we should probably such segments as a 
  55. single attachment when forwarding/replying.  It would also be real nice to
  56. flatten out the nesting in the composer so pieces inside can get snipped.
  57.  
  58. The evolution continues...
  59.  
  60.   =====*/
  61.  
  62.  
  63. #include "headers.h"
  64.  
  65.  
  66. /*
  67.  * Internal Prototypes
  68.  */
  69. void     reply_forward_header PROTO((MAILSTREAM *, long, ENVELOPE *, gf_io_t,
  70.                      char *));
  71. char    *generate_in_reply_to PROTO((ENVELOPE *));
  72. int     fetch_contents PROTO((MAILSTREAM *, long, BODY *, BODY *));
  73. ADDRESS    *reply_cp_addr PROTO((struct pine *, long, char *, ADDRESS *,
  74.                   ADDRESS *, ADDRESS *));
  75. void     reply_fish_personal PROTO((ENVELOPE *, ENVELOPE *));
  76. char    *reply_build_refs PROTO((char *, ENVELOPE *));
  77. int     addr_in_env PROTO((ADDRESS *, ENVELOPE *));
  78. int     addr_lists_same PROTO((ADDRESS *, ADDRESS *));
  79. char    *reply_subject PROTO((char *, char *));
  80. int     reply_poster_followup PROTO((char *));
  81. void     forward_delimiter PROTO((gf_io_t));
  82. char    *forward_subject PROTO((struct pine *, long));
  83. void     bounce_mask_header PROTO((char **, char *));
  84.  
  85.  
  86.  
  87. /*
  88.  * Little defs to keep the code a bit neater...
  89.  */
  90. #define    FRM_PMT    "Use \"Reply-To:\" address instead of \"From:\" address"
  91. #define    ALL_PMT        "Reply to all recipients"
  92. #define    NEWS_PMT    "Follow-up to news group(s), Reply via email to author or Both? "
  93.  
  94. /*
  95.  * standard type of storage object used for body parts...
  96.  */
  97. #if    defined(DOS) && !defined(WIN32)
  98. #define          PART_SO_TYPE    TmpFileStar
  99. #else
  100. #define          PART_SO_TYPE    CharStar
  101. #endif
  102.  
  103.  
  104.  
  105. /*----------------------------------------------------------------------
  106.         Fill in an outgoing message for reply and pass off to send
  107.  
  108.     Args: pine_state -- The usual pine structure
  109.  
  110.   Result: Reply is formatted and passed off to composer/mailer
  111.  
  112. Reply
  113.  
  114.    - put senders address in To field
  115.    - search to and cc fields to see if we aren't the only recipients
  116.    - if other than us, ask if we should reply to all.
  117.    - if answer yes, fill out the To and Cc fields
  118.    - fill in the fcc argument
  119.    - fill in the subject field
  120.    - fill out the body and the attachments
  121.    - pass off to pine_send()
  122.   ---*/
  123. void
  124. reply(pine_state)
  125.      struct pine *pine_state;
  126. {
  127.     ADDRESS   **to_tail, **cc_tail, *atmp,
  128.            *saved_from, *saved_to, *saved_cc, *saved_resent,
  129.           **saved_from_tail, **saved_to_tail, **saved_cc_tail,
  130.           **saved_resent_tail;
  131.     ENVELOPE   *env, *outgoing;
  132.     BODY       *body, *orig_body;
  133.     PART       *part;
  134.     void       *msgtext;
  135.     char       *sig, *tp, *prefix = NULL, *refs = NULL;
  136.     long        msgno, totalm, *seq = NULL;
  137.     int         i, processed_resent, parsed_resent,
  138.             asked_replyall = 0, replytoall = 0, include_text = 0,
  139.         reply_ret = 0, cc_ret = 0, times = -1, ret, warned = 0;
  140.     gf_io_t     pc;
  141.     BUILDER_ARG fcc;
  142. #if    defined(DOS) && !defined(_WINDOWS)
  143.     char *reserve;
  144. #endif
  145. #define NEWSGRPS   0
  146. #define PATH       1
  147. #define REFS       2
  148. #define    FOLLOWUP   3
  149. #define RESENTFROM 4
  150. #define RESENTTO   5
  151. #define RESENTCC   6
  152. #define NEXTRA     7
  153.     char       *hdrs, *values[NEXTRA+1];
  154.     static char *fields[] = {"Newsgroups", "Path", "References", "Followup-To",
  155.                  "Resent-From", "Resent-To", "Resent-Cc", NULL};
  156.     static ESCKEY_S news_opt[] = { {'f', 'f', "F", "Follow-up"},
  157.                    {'r', 'r', "R", "Reply"},
  158.                    {'b', 'b', "B", "Both"},
  159.                    {-1, 0, NULL, NULL} };
  160.  
  161.     outgoing = mail_newenvelope();
  162.     fcc.tptr = NULL; fcc.next = NULL; fcc.xtra = NULL;
  163.     totalm   = mn_total_cur(pine_state->msgmap);
  164.     sig      = get_signature();
  165.     seq         = (long *)fs_get(((size_t)totalm + 1) * sizeof(long));
  166.  
  167.     dprint(4, (debugfile,"\n - reply (%s msgs) -\n", comatose(totalm)));
  168.  
  169.     memset(values, 0, (NEXTRA+1)*sizeof(char *));
  170.     saved_from          = (ADDRESS *)NULL;
  171.     saved_from_tail       = &saved_from;
  172.     saved_to          = (ADDRESS *)NULL;
  173.     saved_to_tail      = &saved_to;
  174.     saved_cc          = (ADDRESS *)NULL;
  175.     saved_cc_tail      = &saved_cc;
  176.     saved_resent      = (ADDRESS *)NULL;
  177.     saved_resent_tail      = &saved_resent;
  178.     outgoing->subject      = NULL;
  179.     ret              = 0;
  180.  
  181.     /*
  182.      * For consistency, the first question is always "include text?"
  183.      */
  184.     if(F_ON(F_AUTO_INCLUDE_IN_REPLY, pine_state)){
  185.     include_text = 1;
  186.     }
  187.     else{
  188.     sprintf(tmp_20k_buf, "Include %s%soriginal message%s in Reply",
  189.         (totalm > 1L) ? comatose(totalm) : "",
  190.         (totalm > 1L) ? " " : "",
  191.         (totalm > 1L) ? "s" : "");
  192.  
  193.     if((ret = want_to(tmp_20k_buf, 'n', 'x', NO_HELP, 0, 0)) == 'x'){
  194.         q_status_message(SM_ORDER, 0, 3,"Reply cancelled");
  195.         goto done_early;
  196.     }
  197.     else
  198.       include_text = ret == 'y';
  199.     }
  200.  
  201.     /*
  202.      * We go to considerable trouble to avoid extra RTT's looking for
  203.      * Resent-* headers if we don't have to.  We piggy-back the request
  204.      * with the Newsgroups request if we're already doing that.  We ask
  205.      * the reply-to-all question as early as possible so that we can avoid
  206.      * looking for Resents if user says no.  However, we're also trying
  207.      * to do this in one pass through the messages, so that adds some
  208.      * complexity.  That's why there are three separate places in the
  209.      * code where we might get the resent headers.
  210.      */
  211.     for(msgno = mn_first_cur(pine_state->msgmap);
  212.     msgno > 0L;
  213.     msgno = mn_next_cur(pine_state->msgmap)){
  214.  
  215.     memset(values, 0, (NEXTRA+1)*sizeof(char *));
  216.     hdrs             = NULL;
  217.     processed_resent = 0;
  218.     parsed_resent    = 0;
  219.     /*--- Grab current envelope ---*/
  220.     env = mail_fetchstructure(pine_state->mail_stream,
  221.                 seq[++times] = mn_m2raw(pine_state->msgmap, msgno),
  222.                 NULL);
  223.     if(!env) {
  224.         q_status_message1(SM_ORDER,3,4,
  225.                   "Error fetching message %s. Can't reply to it.",
  226.                   long2string(msgno));
  227.         goto done_early;
  228.     }
  229.  
  230.     /*
  231.      * If we're talking on an IMAP stream, get the dang newsgroups
  232.      * by hand since this field's not supported by the IMAP driver,
  233.      * otherwise copy the newsgroups if present and fetch the
  234.      * references (another unsupported yet necessary header).
  235.      *
  236.      * However, even if c-client gave us the newsgroup we need to
  237.      * fetch the References and Followup-To headers since they're
  238.      * not yet supported either.
  239.      *
  240.      * NOTE: Don't bother looking if we don't want to know.  In other
  241.      * words, only look if it's our first time thru the loop and we're
  242.      * either replying to a single message or in a newsgroup.  The single
  243.      * message case get's us around the aggregate reply to messages
  244.      * in a mixed mail-news archive where some might have newsgroups
  245.      * and others not or whatever.
  246.      */
  247.     if(times == 0 && (totalm == 1L || IS_NEWS(pine_state->mail_stream))
  248.        && ((env->newsgroups && *env->newsgroups)
  249.            || (pine_state->mail_stream->mailbox[0] == '{'
  250.            || (pine_state->mail_stream->mailbox[0] == '*'
  251.                && pine_state->mail_stream->mailbox[1] == '{')))){
  252.  
  253.         /*
  254.          * Since we're already spending an RTT to get News stuff,
  255.          * get the Resent's that we may need later.
  256.          */
  257.         if(hdrs = xmail_fetchheader_lines(pine_state->mail_stream,
  258.                           seq[times], fields)){
  259.         parsed_resent++;
  260.         simple_header_parse(hdrs, fields, values);
  261.         }
  262.  
  263.         /*
  264.          * If we were given newsgroups OR we hunted (mimicing c-client's
  265.          * rules for bogosity [See c-client/rfc822.c around line 500])
  266.          * and found them AND Followup-To isn't "poster", ask what it
  267.          * is the user wants to do with it...
  268.          */
  269.         if((env->newsgroups
  270.         || (values[NEWSGRPS] && values[NEWSGRPS][0]
  271.             && (values[PATH] && values[PATH][0]
  272.             || (env->message_id
  273.                 && !(strncmp(env->message_id,"<Pine.",6)
  274.                  && strncmp (env->message_id,"<MS-C.",6)
  275.                  && strncmp (env->message_id,"<ML-.",4))))))
  276.            && !(totalm == 1 && reply_poster_followup(values[FOLLOWUP]))){
  277.         /*
  278.          * Now that we know a newsgroups field is present, 
  279.          * ask if the user is posting a follow-up article...
  280.          */
  281.         ret = radio_buttons(NEWS_PMT, -FOOTER_ROWS(pine_state),
  282.                     news_opt, 'r', 'x', NO_HELP, RB_NORM);
  283.  
  284.         if(ret == 'b' || ret == 'f'){
  285.             /* make sure we only send to news */
  286.             if(ret == 'f'){
  287.             mail_free_address(&saved_from);
  288.             mail_free_address(&saved_to);
  289.             mail_free_address(&saved_cc);
  290.             mail_free_address(&saved_resent);
  291.             }
  292.  
  293.             /*
  294.              * Followup-To takes precedence over Newsgroups, if
  295.              * we're not doing aggregate reply...
  296.              */
  297.             if(totalm == 1L && values[FOLLOWUP]
  298.                && values[FOLLOWUP][0]){
  299.             q_status_message(SM_ORDER, 2, 3,
  300.                     "Posting to specified Followup-To groups");
  301.             outgoing->newsgroups = values[FOLLOWUP];
  302.             values[FOLLOWUP] = NULL;
  303.             if(values[NEWSGRPS])
  304.               fs_give((void **)&values[NEWSGRPS]);
  305.             }
  306.             else{
  307.             outgoing->newsgroups = env->newsgroups
  308.                          ? cpystr(env->newsgroups)
  309.                          : values[NEWSGRPS];
  310.             values[NEWSGRPS] = NULL;
  311.             if(values[FOLLOWUP])
  312.               fs_give((void **)&values[FOLLOWUP]);
  313.             }
  314.  
  315.             /* Likewise no refs if aggregate reply! */
  316.             if(totalm == 1L && env->message_id && *env->message_id)
  317.               refs = reply_build_refs(values[REFS], env);
  318.  
  319.             /*
  320.              * General Notes:
  321.              *
  322.              * 1) Seems unlikey anyone will want to both post
  323.              *    news and follow up to the author of the posting
  324.              *       assuming the author reads news.  Well maybe not
  325.              *       so unlikely, but potentially five questions
  326.              *       before posting seems excessive:
  327.              *       1. include orig     2. Follow up      3. Also e-mail
  328.              *       4. use reply-to     5. all recipients
  329.              *
  330.              * 2) BUG: we need to decide what to do if reply in
  331.              *    mixed news/mail folder and some message have
  332.              *    newsgroups and some don't or the ones that do
  333.              *    don't all match.
  334.              */
  335.  
  336.         }
  337.         else if(ret == 'x'){
  338.             q_status_message(SM_ORDER, 0, 3,"Reply cancelled");
  339.             goto done_early;
  340.         }
  341.         }
  342.     }
  343.  
  344.     if(!outgoing->newsgroups || ret == 'b'){
  345.         /*
  346.          * Always use the reply-to if we're replying to more than one 
  347.          * msg...
  348.          */
  349.         if(env->reply_to && !addr_lists_same(env->reply_to, env->from)
  350.            && (F_ON(F_AUTO_REPLY_TO, pine_state)
  351.            || totalm > 1L
  352.            || (reply_ret = want_to(FRM_PMT,'y','x',NO_HELP,0,0))=='y'))
  353.           *saved_from_tail
  354.           = reply_cp_addr(pine_state, seq[times], "reply-to",
  355.                   saved_from, (ADDRESS *)NULL,
  356.                   env->reply_to);
  357.         else
  358.           *saved_from_tail
  359.           = reply_cp_addr(pine_state, seq[times], "From",
  360.                   saved_from, (ADDRESS *)NULL,
  361.                   env->from);
  362.  
  363.         if(reply_ret == 'x') {
  364.         q_status_message(SM_ORDER, 0, 3, "Reply cancelled");
  365.         goto done_early;
  366.         }
  367.  
  368.         while(*saved_from_tail)        /* stay on last address */
  369.           saved_from_tail = &(*saved_from_tail)->next;
  370.  
  371.         /*--------- check for other recipients ---------*/
  372.         if(replytoall || !asked_replyall){
  373.         *saved_to_tail = reply_cp_addr(pine_state, seq[times], "To",
  374.                            saved_to, saved_from, env->to);
  375.                            
  376.  
  377.         while(*saved_to_tail)        /* stay on last address */
  378.           saved_to_tail = &(*saved_to_tail)->next;
  379.         }
  380.  
  381.         if(replytoall || !asked_replyall){
  382.         *saved_cc_tail = reply_cp_addr(pine_state, seq[times], "Cc",
  383.                            saved_cc,
  384.                            saved_from, env->cc);
  385.  
  386.         while(*saved_cc_tail)        /* stay on last address */
  387.           saved_cc_tail = &(*saved_cc_tail)->next;
  388.         }
  389.  
  390.         /*
  391.          * In these cases, we either need to look at the resent headers
  392.          * to include in the reply-to-all, or to decide whether or not
  393.          * we need to ask the reply-to-all question.
  394.          */
  395.         if(replytoall
  396.            || (!asked_replyall
  397.               && ((!saved_from && !saved_cc)
  398.              || (saved_from && !saved_to && !saved_cc)))){
  399.         static char *fakedomain = "@";
  400.  
  401.         /* fetch resent headers */
  402.         if(!hdrs)
  403.           hdrs = xmail_fetchheader_lines(pine_state->mail_stream,
  404.                          seq[times], fields);
  405.         
  406.         processed_resent++;
  407.         if(hdrs){
  408.             if(!parsed_resent)
  409.               simple_header_parse(hdrs, fields, values);
  410.  
  411.             parsed_resent++;
  412.  
  413.             for(i = RESENTFROM; i <= RESENTCC; i++){
  414.             atmp = NULL;
  415.             if(values[i]){
  416.                 removing_trailing_white_space(values[i]);
  417.                 rfc822_parse_adrlist(&atmp, values[i],
  418.                   (F_ON(F_COMPOSE_REJECTS_UNQUAL, pine_state))
  419.                 ? fakedomain : pine_state->maildomain);
  420.                 /*
  421.                  * look for bogus addr entries and replace
  422.                  */
  423.  
  424.  
  425.                 *saved_resent_tail = reply_cp_addr(pine_state,
  426.                                    0, NULL,
  427.                        saved_resent, saved_from, atmp);
  428.                 mail_free_address(&atmp);
  429.                 while(*saved_resent_tail)
  430.                   saved_resent_tail = &(*saved_resent_tail)->next;
  431.             }
  432.             }
  433.         }
  434.         }
  435.  
  436.         /*
  437.          * It makes sense to ask reply-to-all now.
  438.          */
  439.         if(!asked_replyall
  440.               && ((saved_from && (saved_to||saved_cc||saved_resent))
  441.              || (saved_cc || saved_resent))){
  442.           cc_ret = want_to(ALL_PMT,'n','x',NO_HELP,0,0);
  443.           asked_replyall++;
  444.           replytoall = (cc_ret == 'y');
  445.         }
  446.  
  447.         /*
  448.          * If we just answered yes to the reply-to-all question and
  449.          * we still haven't collected the resent headers, do so now.
  450.          */
  451.         if(replytoall && !processed_resent){
  452.         static char *fakedomain = "@";
  453.  
  454.         if(!hdrs)
  455.           hdrs = xmail_fetchheader_lines(pine_state->mail_stream,
  456.                          seq[times], fields);
  457.         processed_resent++;
  458.         if(hdrs){
  459.             if(!parsed_resent)
  460.               simple_header_parse(hdrs, fields, values);
  461.             
  462.             parsed_resent++;
  463.  
  464.             for(i = RESENTFROM; i <= RESENTCC; i++){
  465.             atmp = NULL;
  466.             if(values[i]){
  467.                 removing_trailing_white_space(values[i]);
  468.                 rfc822_parse_adrlist(&atmp, values[i],
  469.                   (F_ON(F_COMPOSE_REJECTS_UNQUAL, pine_state))
  470.                 ? fakedomain : pine_state->maildomain);
  471.  
  472.                 /*
  473.                  * look for bogus addr entries and replace
  474.                  */
  475.  
  476.                 *saved_resent_tail = reply_cp_addr(pine_state,
  477.                                    0, NULL,
  478.                        saved_resent, saved_from, atmp);
  479.                 mail_free_address(&atmp);
  480.                 while(*saved_resent_tail)
  481.                   saved_resent_tail = &(*saved_resent_tail)->next;
  482.             }
  483.             }
  484.         }
  485.         }
  486.  
  487.         if(cc_ret == 'x') {
  488.         q_status_message(SM_ORDER, 0, 3, "Reply cancelled");
  489.         goto done_early;
  490.         }
  491.     }
  492.  
  493.     /*------------ Format the subject line ---------------*/
  494.     if(outgoing->subject){
  495.         /*
  496.          * if reply to more than one message, and all subjects
  497.          * match, so be it.  otherwise set it to something generic...
  498.          */
  499.         if(strucmp(outgoing->subject,
  500.                reply_subject(env->subject, tmp_20k_buf))){
  501.         fs_give((void **)&outgoing->subject);
  502.         outgoing->subject = cpystr("Re: several messages");
  503.         }
  504.     }
  505.     else
  506.       outgoing->subject = reply_subject(env->subject, NULL);
  507.  
  508.     if(hdrs)
  509.       fs_give((void **)&hdrs);
  510.  
  511.     for(i=0; i <= NEXTRA; i++)
  512.       if(values[i])
  513.         fs_give((void **)&values[i]);
  514.     }
  515.  
  516.     to_tail      = &outgoing->to;
  517.     cc_tail      = &outgoing->cc;
  518.     outgoing->to  = (ADDRESS *)NULL;
  519.     outgoing->cc  = (ADDRESS *)NULL;
  520.  
  521.     if(saved_from){
  522.     /* Put From in To. */
  523.     *to_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->to, (ADDRESS *)NULL,
  524.                  saved_from);
  525.     /* and the rest in cc */
  526.     if(replytoall){
  527.         *cc_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->cc, outgoing->to,
  528.                      saved_to);
  529.         while(*cc_tail)        /* stay on last address */
  530.           cc_tail = &(*cc_tail)->next;
  531.  
  532.         *cc_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->cc, outgoing->to,
  533.                      saved_cc);
  534.         while(*cc_tail)
  535.           cc_tail = &(*cc_tail)->next;
  536.  
  537.         *cc_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->cc, outgoing->to,
  538.                      saved_resent);
  539.     }
  540.     }
  541.     else if(saved_to){
  542.     /* No From, put To in To. */
  543.     *to_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->to, (ADDRESS *)NULL,
  544.                  saved_to);
  545.     /* and the rest in cc */
  546.     if(replytoall){
  547.         *cc_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->cc, outgoing->to,
  548.                      saved_cc);
  549.         while(*cc_tail)
  550.           cc_tail = &(*cc_tail)->next;
  551.  
  552.         *cc_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->cc, outgoing->to,
  553.                      saved_resent);
  554.     }
  555.     }
  556.     else{
  557.     /* No From or To, put everything else in To if replytoall, */
  558.     if(replytoall){
  559.         *to_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->to, (ADDRESS *)NULL,
  560.                      saved_cc);
  561.         while(*to_tail)
  562.           to_tail = &(*to_tail)->next;
  563.  
  564.         *to_tail = reply_cp_addr(pine_state, 0, NULL, outgoing->to, (ADDRESS *)NULL,
  565.                      saved_resent);
  566.     }
  567.     /* else, reply to original From which must be us */
  568.     else{
  569.         /*
  570.          * Put self in To if in original From.
  571.          * (notice NULL first arg)
  572.          */
  573.         if(!outgoing->newsgroups)
  574.           *to_tail = reply_cp_addr(NULL, 0, NULL, outgoing->to, (ADDRESS *)NULL,
  575.                        env->from);
  576.     }
  577.     }
  578.  
  579.     /* add any missing personal data */
  580.     reply_fish_personal(outgoing, env);
  581.  
  582.     /* get fcc */
  583.     if(outgoing->to && outgoing->to->host[0] != '.')
  584.       fcc.tptr = get_fcc_based_on_to(outgoing->to);
  585.     else if(outgoing->newsgroups){
  586.     char *errmsg = NULL, *newsgroups_returned = NULL;
  587.     int ret_val;
  588.  
  589.     ret_val = news_build(outgoing->newsgroups, &newsgroups_returned,
  590.                  &errmsg, &fcc);
  591.     if(errmsg){
  592.         if(*errmsg){
  593.         q_status_message1(SM_ORDER, 3, 3, "%s", errmsg);
  594.         display_message(NO_OP_COMMAND);
  595.         }
  596.         fs_give((void **)&errmsg);
  597.     }
  598.     if(ret_val != -1 &&
  599.         strcmp(outgoing->newsgroups, newsgroups_returned)){
  600.         fs_give((void **)&outgoing->newsgroups);
  601.         outgoing->newsgroups = newsgroups_returned;
  602.     }
  603.     else
  604.       fs_give((void **)&newsgroups_returned);
  605.     }
  606.  
  607.     seq[++times] = -1L;        /* mark end of sequence list */
  608.  
  609.     /*==========  Other miscelaneous fields ===================*/   
  610.     outgoing->return_path = (ADDRESS *)NULL;
  611.     outgoing->bcc         = (ADDRESS *)NULL;
  612.     outgoing->sender      = (ADDRESS *)NULL;
  613.     outgoing->return_path = (ADDRESS *)NULL;
  614.     outgoing->in_reply_to = generate_in_reply_to(env);
  615.     outgoing->remail      = NULL;
  616.     outgoing->reply_to    = (ADDRESS *)NULL;
  617.  
  618.     prefix = reply_quote_str(env, times);
  619.     outgoing->message_id  = generate_message_id(pine_state);
  620.  
  621.     if(!outgoing->to &&
  622.        !outgoing->cc &&
  623.        !outgoing->bcc &&
  624.        !outgoing->newsgroups)
  625.       q_status_message(SM_ORDER | SM_DING, 3, 6,
  626.                "Warning: no valid addresses to reply to!");
  627.  
  628. #if    defined(DOS) && !defined(_WINDOWS)
  629. #if    defined(LWP) || defined(PCTCP) || defined(PCNFS)
  630. #define    IN_RESERVE    8192
  631. #else
  632. #define    IN_RESERVE    16384
  633. #endif
  634.     if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
  635.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  636.              "Insufficient memory for message text");
  637.     goto done_early;
  638.     }
  639. #endif
  640.  
  641.    /*==================== Now fix up the message body ====================*/
  642.  
  643.     /* 
  644.      * create storage object to be used for message text
  645.      */
  646.     if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
  647.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  648.              "Error allocating message text");
  649.         goto done_early;
  650.     }
  651.  
  652.     gf_set_so_writec(&pc, (STORE_S *)msgtext);
  653.  
  654.     /*---- Include the original text if requested ----*/
  655.     if(include_text){
  656.     /* write preliminary envelope info into message text */
  657.     if(F_OFF(F_SIG_AT_BOTTOM, pine_state)){
  658.         if(sig[0]){
  659.         so_puts((STORE_S *)msgtext, sig);
  660.         fs_give((void **)&sig);
  661.         }
  662.         else
  663.           so_puts((STORE_S *)msgtext, NEWLINE);
  664.  
  665.         so_puts((STORE_S *)msgtext, NEWLINE);
  666.     }
  667.  
  668.     if(totalm > 1L){
  669.         body                  = mail_newbody();
  670.         body->type            = TYPETEXT;
  671.         body->contents.binary = msgtext;
  672.         env              = NULL;
  673.  
  674.         for(msgno = mn_first_cur(pine_state->msgmap);
  675.         msgno > 0L;
  676.         msgno = mn_next_cur(pine_state->msgmap)){
  677.  
  678.         if(env){            /* put 2 between messages */
  679.             gf_puts(NEWLINE, pc);
  680.             gf_puts(NEWLINE, pc);
  681.         }
  682.  
  683.         /*--- Grab current envelope ---*/
  684.         env = mail_fetchstructure(pine_state->mail_stream,
  685.                       mn_m2raw(pine_state->msgmap, msgno),
  686.                       &orig_body);
  687.         if(!env || !orig_body){
  688.             q_status_message1(SM_ORDER,3,4,
  689.                   "Error fetching message %s. Can't reply to it.",
  690.                   long2string(mn_get_cur(pine_state->msgmap)));
  691.             goto done_early;
  692.         }
  693.  
  694.         if(orig_body == NULL || orig_body->type == TYPETEXT) {
  695.             reply_delimiter(env, pc);
  696.             if(F_ON(F_INCLUDE_HEADER, pine_state))
  697.               reply_forward_header(pine_state->mail_stream,
  698.                        mn_m2raw(pine_state->msgmap,msgno),
  699.                        env, pc, prefix);
  700.  
  701.             get_body_part_text(pine_state->mail_stream, orig_body,
  702.                        mn_m2raw(pine_state->msgmap, msgno),
  703.                        "1", pc, prefix, ". Text not included");
  704.         } else if(orig_body->type == TYPEMULTIPART) {
  705.             if(!warned++)
  706.               q_status_message(SM_ORDER,3,7,
  707.               "WARNING!  Attachments not included in multiple reply.");
  708.  
  709.             if(orig_body->contents.part &&
  710.                orig_body->contents.part->body.type == TYPETEXT) {
  711.             /*---- First part of the message is text -----*/
  712.             reply_delimiter(env, pc);
  713.             if(F_ON(F_INCLUDE_HEADER, pine_state))
  714.               reply_forward_header(pine_state->mail_stream,
  715.                            mn_m2raw(pine_state->msgmap,
  716.                             msgno),
  717.                            env, pc, prefix);
  718.  
  719.             get_body_part_text(pine_state->mail_stream,
  720.                        &orig_body->contents.part->body,
  721.                        mn_m2raw(pine_state->msgmap, msgno),
  722.                        "1", pc, prefix,
  723.                        ". Text not included");
  724.             } else {
  725.             q_status_message(SM_ORDER,0,3,
  726.                      "Multipart with no leading text part!");
  727.             }
  728.         } else {
  729.             /*---- Single non-text message of some sort ----*/
  730.             q_status_message(SM_ORDER,3,3,
  731.                      "Non-text message not included!");
  732.         }
  733.         }
  734.     }
  735.     else{
  736.         msgno = mn_m2raw(pine_state->msgmap,
  737.                  mn_get_cur(pine_state->msgmap));
  738.  
  739.         /*--- Grab current envelope ---*/
  740.         env = mail_fetchstructure(pine_state->mail_stream, msgno,
  741.                       &orig_body);
  742.         if(!env || !orig_body) {
  743.         q_status_message1(SM_ORDER,3,4,
  744.                   "Error fetching message %s. Can't reply to it.",
  745.                   long2string(mn_get_cur(pine_state->msgmap)));
  746.         goto done_early;
  747.         }
  748.  
  749.         if(orig_body == NULL || orig_body->type == TYPETEXT
  750.            || F_OFF(F_ATTACHMENTS_IN_REPLY, pine_state)){
  751.  
  752.         /*------ Simple text-only message ----*/
  753.         body                  = mail_newbody();
  754.         body->type            = TYPETEXT;
  755.         body->contents.binary = msgtext;
  756.         reply_delimiter(env, pc);
  757.         if(F_ON(F_INCLUDE_HEADER, pine_state))
  758.           reply_forward_header(pine_state->mail_stream, msgno,
  759.                        env, pc, prefix);
  760.  
  761.         if(!orig_body || orig_body->type == TYPETEXT){
  762.             get_body_part_text(pine_state->mail_stream, orig_body,
  763.                        msgno, "1", pc, prefix,
  764.                        ". Text not included");
  765.         }
  766.         else if(orig_body->type == TYPEMULTIPART
  767.             && orig_body->contents.part->body.type == TYPETEXT){
  768.             get_body_part_text(pine_state->mail_stream,
  769.                        &orig_body->contents.part->body,
  770.                        msgno, "1", pc, prefix,
  771.                        ". Text not included");
  772.         }
  773.         else{
  774.             gf_puts(NEWLINE, pc);
  775.             gf_puts("  [NON-Text Body part not included]", pc);
  776.             gf_puts(NEWLINE, pc);
  777.         }
  778.         } else if(orig_body->type == TYPEMULTIPART) {
  779.  
  780.         /*------ Message is Multipart ------*/
  781. /* BUG: need to do a better job for MULTIPART/alternate --*/
  782.         body = copy_body(NULL, orig_body);
  783.  
  784.         if(orig_body->contents.part &&
  785.            orig_body->contents.part->body.type == TYPETEXT) {
  786.             /*---- First part of the message is text -----*/
  787.             body->contents.part->body.contents.binary = msgtext;
  788. /* BUG: ? matter that we're not setting body.size.bytes */
  789.             reply_delimiter(env, pc);
  790.             if(F_ON(F_INCLUDE_HEADER, pine_state))
  791.               reply_forward_header(pine_state->mail_stream, msgno,
  792.                        env, pc, prefix);
  793.  
  794.             get_body_part_text(pine_state->mail_stream, 
  795.                        &orig_body->contents.part->body,
  796.                        msgno, "1", pc, prefix,
  797.                        ". Text not included");
  798.             if(!fetch_contents(pine_state->mail_stream,msgno,body,
  799.                        body))
  800.               q_status_message(SM_ORDER | SM_DING, 3, 4,
  801.                        "Error including all message parts");
  802.         } else {
  803.             /*--- Fetch the original pieces ---*/
  804.             if(!fetch_contents(pine_state->mail_stream, msgno, body,
  805.                        body))
  806.               q_status_message(SM_ORDER | SM_DING, 3, 4,
  807.                        "Error including all message parts");
  808.  
  809.             /*--- No text part, create a blank one ---*/
  810.             part                       = mail_newbody_part();
  811.             part->next                 = body->contents.part;
  812.             body->contents.part        = part;
  813.             part->body.contents.binary = msgtext;
  814. /* BUG: ? matter that we're not setting body.size.bytes */
  815.         }
  816.         } else {
  817.         /*---- Single non-text message of some sort ----*/
  818.         body                = mail_newbody();
  819.         body->type          = TYPEMULTIPART;
  820.         part                = mail_newbody_part();
  821.         body->contents.part = part;
  822.     
  823.         /*--- The first part, a blank text part to be edited ---*/
  824.         part->body.type            = TYPETEXT;
  825.         part->body.contents.binary = msgtext;
  826. /* BUG: ? matter that we're not setting body.size.bytes */
  827.  
  828.         /*--- The second part, what ever it is ---*/
  829.         part->next           = mail_newbody_part();
  830.         part                 = part->next;
  831.         part->body.id
  832.                      = generate_message_id(pine_state);
  833.         copy_body(&(part->body), orig_body);
  834.         /*
  835.          * the idea here is to fetch part into storage object
  836.          */
  837.         if(part->body.contents.binary = (void *) so_get(PART_SO_TYPE,
  838.                                 NULL,EDIT_ACCESS)){
  839. #if    defined(DOS) && !defined(WIN32)
  840.             mail_parameters(ps_global->mail_stream, SET_GETS,
  841.                     (void *)dos_gets); /* fetched to disk */
  842.             append_file = (FILE *)so_text(
  843.                     (STORE_S *)part->body.contents.binary);
  844.  
  845.             if(mail_fetchbody(pine_state->mail_stream, msgno, "1",
  846.                       &part->body.size.bytes) == NULL)
  847.               goto done;
  848.  
  849.             mail_parameters(ps_global->mail_stream, SET_GETS,
  850.                     (void *)NULL);
  851.             append_file = NULL;
  852.             mail_gc(pine_state->mail_stream, GC_TEXTS);
  853. #else
  854.             if((tp=mail_fetchbody(pine_state->mail_stream, msgno, "1",
  855.                       &part->body.size.bytes)) == NULL)
  856.               goto done;
  857.             so_puts((STORE_S *)part->body.contents.binary, tp);
  858. #endif
  859.         }
  860.         else
  861.           goto done;
  862.         }
  863.     }
  864.     } else {
  865.         /*--------- No text included --------*/
  866.         body                  = mail_newbody();
  867.         body->type            = TYPETEXT;
  868.     body->contents.binary = msgtext;
  869.     }
  870.  
  871.     if(sig)
  872.       so_puts((STORE_S *)msgtext, sig);
  873.  
  874. #if    defined(DOS) && !defined(_WINDOWS)
  875.     free((void *)reserve);
  876. #endif
  877.  
  878.     /* partially formatted outgoing message */
  879.     pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
  880.           fcc.tptr, seq, refs, prefix, NULL, NULL, 0);
  881.   done:
  882.     pine_free_body(&body);
  883.   done_early:
  884.     mail_free_envelope(&outgoing);
  885.     mail_free_address(&saved_from);
  886.     mail_free_address(&saved_to);
  887.     mail_free_address(&saved_cc);
  888.     mail_free_address(&saved_resent);
  889.     fs_give((void **)&seq);
  890.  
  891.     if(refs)
  892.       fs_give((void **)&refs);
  893.  
  894.     if(prefix)
  895.       fs_give((void **)&prefix);
  896.  
  897.     if(fcc.tptr)
  898.       fs_give((void **)&fcc.tptr);
  899.  
  900.     if(sig)
  901.       fs_give((void **)&sig);
  902.  
  903.     for(i=0; i <= NEXTRA; i++)
  904.       if(values[i])
  905.     fs_give((void **)&values[i]);
  906. }
  907.  
  908.  
  909.  
  910. /*----------------------------------------------------------------------
  911.     Return a pointer to a copy of the given address list
  912.   filtering out those already in the "mask" lists and ourself.
  913.  
  914. Args:  mask1  -- Don't copy if in this list
  915.        mask2  --  or if in this list
  916.        source -- List to be copied
  917.  
  918.   ---*/
  919. ADDRESS *
  920. reply_cp_addr(ps, msgno, field, mask1, mask2, source)
  921.      struct pine *ps;
  922.      long      msgno;
  923.      char     *field;
  924.      ADDRESS     *mask1, *mask2, *source;
  925. {
  926.     ADDRESS *tmp1, *tmp2, *ret = NULL, **ret_tail;
  927.     char    *p;
  928.  
  929.     for(tmp1 = source; msgno && tmp1; tmp1 = tmp1->next)
  930.       if(tmp1->host && tmp1->host[0] == '.'){
  931.       char *h, *fields[2];
  932.  
  933.       fields[0] = field;
  934.       fields[1] = NULL;
  935.       if(h = xmail_fetchheader_lines(ps->mail_stream, msgno, fields)){
  936.           char *p, fname[32];
  937.  
  938.           sprintf(fname, "%s:", field);
  939.           for(p = h; p = strstr(p, fname); )
  940.         rplstr(p, strlen(fname), "");    /* strip field strings */
  941.  
  942.           sqznewlines(h);            /* blat out CR's & LF's */
  943.           for(p = h; p = strchr(p, TAB); )
  944.         *p++ = ' ';            /* turn TABs to whitespace */
  945.  
  946.           if(*h){
  947.           long l;
  948.  
  949.           ret = (ADDRESS *) fs_get(sizeof(ADDRESS));
  950.           memset(ret, 0, sizeof(ADDRESS));
  951.  
  952.           /* base64 armor plate the gunk to protect against
  953.            * c-client quoting in output.
  954.            */
  955.           p = (char *) rfc822_binary(h, strlen(h),
  956.                          (unsigned long *) &l);
  957.           fs_give((void **) &h);
  958.           ret->mailbox = (char *) fs_get(strlen(p) + 4);
  959.           sprintf(ret->mailbox, "&%s", p);
  960.           fs_give((void **) &p);
  961.           ret->host = cpystr(".RAW-FIELD.");
  962.           }
  963.       }
  964.  
  965.       return(ret);
  966.       }
  967.  
  968.     ret_tail = &ret;
  969.     for(source = first_addr(source); source; source = source->next){
  970.     for(tmp1 = first_addr(mask1); tmp1; tmp1 = tmp1->next)
  971.       if(address_is_same(source, tmp1))
  972.         break;
  973.  
  974.     for(tmp2 = first_addr(mask2); tmp2; tmp2 = tmp2->next)
  975.       if(address_is_same(source, tmp2))
  976.         break;
  977.  
  978.     /*
  979.      * If there's no match in masks *and* this address isn't us, copy...
  980.      */
  981.     if(!tmp1 && !tmp2 && (!ps || !address_is_us(source, ps))){
  982.         tmp1         = source->next;
  983.         source->next = NULL;    /* only copy one addr! */
  984.         *ret_tail    = rfc822_cpy_adr(source);
  985.         ret_tail     = &(*ret_tail)->next;
  986.         source->next = tmp1;    /* restore rest of list */
  987.     }
  988.     }
  989.  
  990.     return(ret);
  991. }
  992.  
  993.  
  994.  
  995. /*----------------------------------------------------------------------
  996.     Test the given address lists for equivalence
  997.  
  998. Args:  x -- First address list for comparison
  999.        y -- Second address for comparison
  1000.  
  1001.   ---*/
  1002. int
  1003. addr_lists_same(x, y)
  1004.      ADDRESS *x, *y;
  1005. {
  1006.     for(x = first_addr(x), y = first_addr(y); x && y; x = x->next, y = y->next)
  1007.       if(!address_is_same(x, y))
  1008.     return(0);
  1009.  
  1010.     return(!x && !y);            /* true if ran off both lists */
  1011. }
  1012.  
  1013.  
  1014.  
  1015. /*----------------------------------------------------------------------
  1016.     Test the given address against those in the given envelope's to, cc
  1017.  
  1018. Args:  addr -- address for comparison
  1019.        env  -- envelope to compare against
  1020.  
  1021.   ---*/
  1022. int
  1023. addr_in_env(addr, env)
  1024.     ADDRESS  *addr;
  1025.     ENVELOPE *env;
  1026. {
  1027.     ADDRESS *ap;
  1028.  
  1029.     for(ap = env ? env->to : NULL; ap; ap = ap->next)
  1030.       if(address_is_same(addr, ap))
  1031.     return(1);
  1032.  
  1033.     for(ap = env ? env->cc : NULL; ap; ap = ap->next)
  1034.       if(address_is_same(addr, ap))
  1035.     return(1);
  1036.  
  1037.     return(0);                /* not found! */
  1038. }
  1039.  
  1040.  
  1041.  
  1042. /*----------------------------------------------------------------------
  1043.     Add missing personal info dest from src envelope
  1044.  
  1045. Args:  dest -- envelope to add personal info to
  1046.        src  -- envelope to get personal info from
  1047.  
  1048. NOTE: This is just kind of a courtesy function.  It's really not adding
  1049.       anything needed to get the mail thru, but it is nice for the user
  1050.       under some odd circumstances.
  1051.   ---*/
  1052. void
  1053. reply_fish_personal(dest, src)
  1054.      ENVELOPE *dest, *src;
  1055. {
  1056.     ADDRESS *da, *sa;
  1057.  
  1058.     for(da = dest ? dest->to : NULL; da; da = da->next){
  1059.     for(sa = src ? src->to : NULL; sa && !da->personal ; sa = sa->next)
  1060.       if(address_is_same(da, sa) && sa->personal)
  1061.         da->personal = cpystr(sa->personal);
  1062.  
  1063.     for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
  1064.       if(address_is_same(da, sa) && sa->personal)
  1065.         da->personal = cpystr(sa->personal);
  1066.     }
  1067.  
  1068.     for(da = dest ? dest->cc : NULL; da; da = da->next){
  1069.     for(sa = src ? src->to : NULL; sa && !da->personal; sa = sa->next)
  1070.       if(address_is_same(da, sa) && sa->personal)
  1071.         da->personal = cpystr(sa->personal);
  1072.  
  1073.     for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
  1074.       if(address_is_same(da, sa) && sa->personal)
  1075.         da->personal = cpystr(sa->personal);
  1076.     }
  1077. }
  1078.  
  1079.  
  1080. /*----------------------------------------------------------------------
  1081.    Given a header field and envelope, build "References: " header data
  1082.  
  1083. Args:  
  1084.  
  1085. Returns: 
  1086.   ---*/
  1087. char *
  1088. reply_build_refs(h, env)
  1089.     char     *h;
  1090.     ENVELOPE *env;
  1091. {
  1092.     int   l, id_len = strlen(env->message_id);
  1093.     char *p, *refs = NULL;
  1094.  
  1095.     if(h){
  1096.     while((l = strlen(h)) + id_len + 1 > 512 && (p = strstr(h, "> <")))
  1097.       h = p + 2;
  1098.  
  1099.     refs = fs_get(id_len + l + 2);
  1100.     sprintf(refs, "%s %s", h, env->message_id);
  1101.     }
  1102.  
  1103.     if(!refs && id_len)
  1104.       refs = cpystr(env->message_id);
  1105.  
  1106.     return(refs);
  1107. }
  1108.  
  1109.  
  1110.  
  1111. /*----------------------------------------------------------------------
  1112.     Format and return subject suitable for the reply command
  1113.  
  1114. Args:  subject -- subject to build reply subject for
  1115.        buf -- buffer to use for writing.  If non supplied, alloc one.
  1116.  
  1117. Returns: with either "Re:" prepended or not, if already there.
  1118.          returned string is allocated.
  1119.   ---*/
  1120. char *
  1121. reply_subject(subject, buf)
  1122.      char *subject;
  1123.      char *buf;
  1124. {
  1125.     size_t  l   = (subject && *subject) ? strlen(subject) : 10;
  1126.     char   *tmp = fs_get(l + 1), *decoded;
  1127.  
  1128.     if(!buf)
  1129.       buf = fs_get(l + 5);
  1130.  
  1131.     /* decode any 8bit into tmp buffer */
  1132.     decoded = (char *) rfc1522_decode((unsigned char *)tmp, subject, NULL);
  1133.  
  1134.     if(decoded                    /* already "re:" ? */
  1135.        && (decoded[0] == 'R' || decoded[0] == 'r')
  1136.        && (decoded[1] == 'E' || decoded[1] == 'e')
  1137.        &&  decoded[2] == ':')
  1138.       strcpy(buf, subject);
  1139.     else
  1140.       sprintf(buf, "Re: %s", (subject && *subject) ? subject : "your mail");
  1141.  
  1142.     fs_give((void **) &tmp);
  1143.     return(buf);
  1144. }
  1145.  
  1146.  
  1147.  
  1148. /*----------------------------------------------------------------------
  1149.     return a suitable quoting string "> " by default for replied text
  1150.  
  1151. Args:  env -- envelope of message being replied to
  1152.  
  1153. Returns: malloc'd array containing quoting string
  1154.   ---*/
  1155. char *
  1156. reply_quote_str(env, times)
  1157.     ENVELOPE *env;
  1158.     int          times;
  1159. {
  1160.     char *prefix, *p;
  1161.  
  1162.     /* set up the prefix to quote included text */
  1163.     if(p = strstr(ps_global->VAR_REPLY_STRING, "_FROM_")){
  1164.     char *from_name = (times > 1) ? "Many"
  1165.                       : env->from->mailbox
  1166.                      ? env->from->mailbox : "";
  1167.  
  1168.     prefix = (char *)fs_get((strlen(ps_global->VAR_REPLY_STRING)
  1169.                 + strlen(from_name)) * sizeof(char));
  1170.     strcpy(prefix, ps_global->VAR_REPLY_STRING);
  1171.     rplstr(prefix + (p - ps_global->VAR_REPLY_STRING), 6, from_name);
  1172.     removing_quotes(prefix);
  1173.     }
  1174.     else
  1175.       prefix = removing_quotes(cpystr(ps_global->VAR_REPLY_STRING));
  1176.  
  1177.     return(prefix);
  1178. }
  1179.  
  1180.  
  1181.  
  1182. /*
  1183.  * reply_delimeter - output formatted reply delimiter for given envelope
  1184.  *             with supplied character writing function.
  1185.  */
  1186. void
  1187. reply_delimiter(env, pc)
  1188.     ENVELOPE *env;
  1189.     gf_io_t   pc;
  1190. {
  1191.     struct date d;
  1192.  
  1193.     parse_date(env->date, &d);
  1194.     gf_puts("On ", pc);                /* All delims have... */
  1195.     if(d.wkday != -1){                /* "On day, date month year" */
  1196.     gf_puts(week_abbrev(d.wkday), pc);    /* in common */
  1197.     gf_puts(", ", pc);
  1198.     }
  1199.  
  1200.     gf_puts(int2string(d.day), pc);
  1201.     (*pc)(' ');
  1202.     gf_puts(month_abbrev(d.month), pc);
  1203.     (*pc)(' ');
  1204.     gf_puts(int2string(d.year), pc);
  1205.  
  1206.     /* but what follows, depends */
  1207.     if(!env->from || (env->from->host && env->from->host[0] == '.')){
  1208.     gf_puts(", it was written:", pc);
  1209.     }
  1210.     else if (env->from->personal) {
  1211.     gf_puts(", ", pc);
  1212.     gf_puts((char *) rfc1522_decode((unsigned char *) tmp_20k_buf,
  1213.                     env->from->personal, NULL), pc);
  1214.     gf_puts(" wrote:", pc);
  1215.     }
  1216.     else {
  1217.     (*pc)(' ');
  1218.     gf_puts(env->from->mailbox, pc);
  1219.     if(env->from->host){
  1220.         (*pc)('@');
  1221.         gf_puts(env->from->host, pc);
  1222.     }
  1223.  
  1224.     gf_puts(" wrote:", pc);
  1225.     }
  1226.  
  1227.     gf_puts(NEWLINE, pc);            /* and end with two newlines */
  1228.     gf_puts(NEWLINE, pc);
  1229. }
  1230.  
  1231.  
  1232. /*
  1233.  * reply_poster_followup - return TRUE if "followup-to" set to "poster"
  1234.  *
  1235.  * NOTE: queues status message indicating such
  1236.  */
  1237. int
  1238. reply_poster_followup(s)
  1239.     char *s;
  1240. {
  1241.     if(s && !strucmp(s, "poster")){
  1242.     q_status_message(SM_ORDER, 2, 3,
  1243.              "Replying to Poster as specified in \"Followup-To\"");
  1244.     return(1);
  1245.     }
  1246.  
  1247.     return(0);
  1248. }
  1249.  
  1250.  
  1251.  
  1252. /*
  1253.  * forward_delimiter - return delimiter for forwarded text
  1254.  */
  1255. void
  1256. forward_delimiter(pc)
  1257.     gf_io_t pc;
  1258. {
  1259.     gf_puts(NEWLINE, pc);
  1260.     gf_puts("---------- Forwarded message ----------", pc);
  1261.     gf_puts(NEWLINE, pc);
  1262. }
  1263.  
  1264.  
  1265.  
  1266. /*----------------------------------------------------------------------
  1267.     Wrapper for header formatting tool
  1268.  
  1269.     Args: stream --
  1270.       msgno --
  1271.       env --
  1272.       pc --
  1273.       prefix --
  1274.  
  1275.   Result: header suitable for reply/forward text written using "pc"
  1276.  
  1277.   ----------------------------------------------------------------------*/
  1278. void
  1279. reply_forward_header(stream, msgno, env, pc, prefix)
  1280.     MAILSTREAM *stream;
  1281.     long    msgno;
  1282.     ENVELOPE   *env;
  1283.     gf_io_t    pc;
  1284.     char       *prefix;
  1285. {
  1286.     int rv;
  1287.     HEADER_S h;
  1288.  
  1289.     HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
  1290.         FE_DEFAULT & ~FE_BCC);
  1291.     if(rv = format_header_text(stream, msgno, env, &h, pc, prefix)){
  1292.     if(rv == 1)
  1293.       gf_puts("  [Error fetching message header data]", pc);
  1294.     }
  1295.     else{
  1296.     if(prefix)            /* prefix to write? */
  1297.       gf_puts(prefix, pc);
  1298.  
  1299.     gf_puts(NEWLINE, pc);        /* write header delimiter */
  1300.     }
  1301. }
  1302.     
  1303.  
  1304.  
  1305. /*----------------------------------------------------------------------
  1306.        Partially set up message to forward and pass off to composer/mailer
  1307.  
  1308.     Args: pine_state -- The usual pine structure
  1309.  
  1310.   Result: outgoing envelope and body created and passed off to composer/mailer
  1311.  
  1312.    Create the outgoing envelope for the mail being forwarded, which is 
  1313. not much more than filling in the subject, and create the message body
  1314. of the outgoing message which requires formatting the header from the
  1315. envelope of the original messasge.
  1316.   ----------------------------------------------------------------------*/
  1317. void
  1318. forward(pine_state)
  1319.     struct pine   *pine_state;
  1320. {
  1321.     ENVELOPE      *env, *outgoing;
  1322.     BODY          *body, *orig_body, *text_body, *b;
  1323.     char          *tmp_text, *sig;
  1324.     long           msgno, totalmsgs;
  1325.     PART          *part;
  1326.     MAILSTREAM    *stream;
  1327.     void          *msgtext;
  1328.     gf_io_t        pc;
  1329.     int            ret;
  1330. #if    defined(DOS) && !defined(_WINDOWS)
  1331.     char      *reserve;
  1332. #endif
  1333.  
  1334.     dprint(4, (debugfile, "\n - forward -\n"));
  1335.  
  1336.     stream                = pine_state->mail_stream;
  1337.     outgoing              = mail_newenvelope();
  1338.     outgoing->message_id  = generate_message_id(pine_state);
  1339.  
  1340.     if((totalmsgs = mn_total_cur(pine_state->msgmap)) > 1L){
  1341.     sprintf(tmp_20k_buf, "%s forwarded messages...", comatose(totalmsgs));
  1342.     outgoing->subject = cpystr(tmp_20k_buf);
  1343.     }
  1344.     else{
  1345.     /*---------- Get the envelope of message we're forwarding ------*/
  1346.     msgno = mn_m2raw(pine_state->msgmap, mn_get_cur(pine_state->msgmap));
  1347.     if(!(outgoing->subject = forward_subject(pine_state, msgno))){
  1348.         q_status_message1(SM_ORDER,3,4,
  1349.                   "Error fetching message %s. Can't forward it.",
  1350.                   long2string(msgno));
  1351.         goto clean_early;
  1352.     }
  1353.     }
  1354.  
  1355.     /*
  1356.      * as with all text bound for the composer, build it in 
  1357.      * a storage object of the type it understands...
  1358.      */
  1359.     if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
  1360.     q_status_message(SM_ORDER | SM_DING, 3, 4,
  1361.              "Error allocating message text");
  1362.     goto clean_early;
  1363.     }
  1364.  
  1365.     
  1366.     so_puts((STORE_S *)msgtext, *(sig = get_signature()) ? sig : NEWLINE);
  1367.     fs_give((void **)&sig);
  1368.     gf_set_so_writec(&pc, (STORE_S *)msgtext);
  1369.  
  1370. #if    defined(DOS) && !defined(_WINDOWS)
  1371. #if    defined(LWP) || defined(PCTCP) || defined(PCNFS)
  1372. #define    IN_RESERVE    8192
  1373. #else
  1374. #define    IN_RESERVE    16384
  1375. #endif
  1376.     if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
  1377.     so_give(&(STORE_S *)msgtext);
  1378.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  1379.              "Insufficient memory for message text");
  1380.     goto clean_early;
  1381.     }
  1382. #endif
  1383.  
  1384.     ret = (totalmsgs > 1L)
  1385.        ? want_to("Forward messages as a MIME digest",'y','x',NO_HELP,0,0)
  1386.        : (pine_state->full_header)
  1387.          ? want_to("Forward message as an attachment",'n','x',NO_HELP,0,0)
  1388.          : 0;
  1389.  
  1390.     /*
  1391.      * If we're forwarding multiple messages *or* the forward-as-mime
  1392.      * is turned on and the users wants it done that way, package things
  1393.      * up...
  1394.      */
  1395.     if(ret == 'x'){
  1396.     q_status_message(SM_ORDER, 0, 3, "Forward message cancelled");
  1397.     goto clean_early;
  1398.     }
  1399.     else if(ret == 'y'){            /* attach message[s]!!! */
  1400.     PART **pp;
  1401.     long   totalsize = 0L;
  1402.  
  1403.     /*---- New Body to start with ----*/
  1404.         body           = mail_newbody();
  1405.         body->type     = TYPEMULTIPART;
  1406.  
  1407.         /*---- The TEXT part/body ----*/
  1408.         body->contents.part                       = mail_newbody_part();
  1409.         body->contents.part->body.type            = TYPETEXT;
  1410.         body->contents.part->body.contents.binary = msgtext;
  1411.  
  1412.     if(totalmsgs > 1L){
  1413.         /*---- The MULTIPART/DIGEST part ----*/
  1414.         body->contents.part->next            = mail_newbody_part();
  1415.         body->contents.part->next->body.type    = TYPEMULTIPART;
  1416.         body->contents.part->next->body.subtype    = cpystr("Digest");
  1417.         sprintf(tmp_20k_buf, "Digest of %s messages", comatose(totalmsgs));
  1418.         body->contents.part->next->body.description = cpystr(tmp_20k_buf);
  1419.         pp = &(body->contents.part->next->body.contents.part);
  1420.     }
  1421.     else
  1422.       pp = &(body->contents.part->next);
  1423.  
  1424.     /*---- The Message body subparts ----*/
  1425.     for(msgno = mn_first_cur(pine_state->msgmap);
  1426.         msgno > 0L;
  1427.         msgno = mn_next_cur(pine_state->msgmap)){
  1428.         msgno         = mn_m2raw(pine_state->msgmap, msgno);
  1429.         *pp             = mail_newbody_part();
  1430.         b             = &((*pp)->body);
  1431.         pp             = &((*pp)->next);
  1432.         b->type         = TYPEMESSAGE;
  1433.         b->id         = generate_message_id(pine_state);
  1434.         b->description       = forward_subject(pine_state, msgno);
  1435.         b->contents.msg.env  = NULL;
  1436.         b->contents.msg.body = NULL;
  1437.  
  1438.         /*---- Package each message in a storage object ----*/
  1439.         if((b->contents.binary = (void *) so_get(PART_SO_TYPE, NULL,
  1440.                              EDIT_ACCESS)) == NULL)
  1441.           goto bomb;
  1442.  
  1443.         /* write the header */
  1444.         if((tmp_text = mail_fetchheader(stream, msgno)) && *tmp_text)
  1445.           so_puts((STORE_S *)b->contents.binary, tmp_text);
  1446.         else
  1447.           goto bomb;
  1448.  
  1449. #if    defined(DOS) && !defined(WIN32)
  1450.         /* write fetched text to disk */
  1451.         mail_parameters(stream, SET_GETS, (void *)dos_gets);
  1452.         append_file = (FILE *)so_text((STORE_S *)b->contents.binary);
  1453.  
  1454.         /* HACK!  See mailview.c:format_message for details... */
  1455.         stream->text = NULL;
  1456.         /* write the body */
  1457.         if(mail_fetchtext(stream, msgno) == NULL)
  1458.           goto bomb;
  1459.  
  1460.         b->size.bytes = ftell(append_file);
  1461.         /* next time body may stay in core */
  1462.         mail_parameters(stream, SET_GETS, (void *)NULL);
  1463.         append_file   = NULL;
  1464.         mail_gc(stream, GC_TEXTS);
  1465.         so_release((STORE_S *)b->contents.binary);
  1466. #else
  1467.         b->size.bytes = strlen(tmp_text);
  1468.         so_puts((STORE_S *)b->contents.binary, "\015\012");
  1469.         if(tmp_text = mail_fetchtext(stream, msgno)){
  1470.         if(*tmp_text)
  1471.           so_puts((STORE_S *)b->contents.binary, tmp_text);
  1472.         }
  1473.         else
  1474.           goto bomb;
  1475.  
  1476.         b->size.bytes += strlen(tmp_text);
  1477. #endif
  1478.         totalsize += b->size.bytes;
  1479.     }
  1480.  
  1481.     if(totalmsgs > 1L)
  1482.       body->contents.part->next->body.size.bytes = totalsize;
  1483.  
  1484.     }
  1485.     else if(totalmsgs > 1L){
  1486.     int                warned = 0;
  1487.     body                  = mail_newbody();
  1488.     body->type            = TYPETEXT;
  1489.     body->contents.binary = msgtext;
  1490.     env              = NULL;
  1491.  
  1492.     for(msgno = mn_first_cur(pine_state->msgmap);
  1493.         msgno > 0L;
  1494.         msgno = mn_next_cur(pine_state->msgmap)){
  1495.  
  1496.         if(env){            /* put 2 between messages */
  1497.         gf_puts(NEWLINE, pc);
  1498.         gf_puts(NEWLINE, pc);
  1499.         }
  1500.  
  1501.         /*--- Grab current envelope ---*/
  1502.         env = mail_fetchstructure(pine_state->mail_stream,
  1503.                       mn_m2raw(pine_state->msgmap, msgno),
  1504.                       &orig_body);
  1505.         if(!env || !orig_body){
  1506.         q_status_message1(SM_ORDER,3,4,
  1507.                    "Error fetching message %s. Can't forward it.",
  1508.                    long2string(msgno));
  1509.         goto bomb;
  1510.         }
  1511.  
  1512.         if(orig_body == NULL || orig_body->type == TYPETEXT) {
  1513.         if(!pine_state->anonymous){
  1514.             forward_delimiter(pc);
  1515.             reply_forward_header(pine_state->mail_stream,
  1516.                      mn_m2raw(pine_state->msgmap, msgno),
  1517.                      env, pc, "");
  1518.         }
  1519.  
  1520.         if(!get_body_part_text(pine_state->mail_stream, orig_body,
  1521.                        mn_m2raw(pine_state->msgmap, msgno),
  1522.                        "1", pc, "", ". Text not included"))
  1523.           goto bomb;
  1524.         } else if(orig_body->type == TYPEMULTIPART) {
  1525.         if(!warned++)
  1526.           q_status_message(SM_ORDER,3,7,
  1527.             "WARNING!  Attachments not included in multiple forward.");
  1528.  
  1529.         if(orig_body->contents.part &&
  1530.            orig_body->contents.part->body.type == TYPETEXT) {
  1531.             /*---- First part of the message is text -----*/
  1532.             forward_delimiter(pc);
  1533.             reply_forward_header(pine_state->mail_stream,
  1534.                      mn_m2raw(pine_state->msgmap,msgno),
  1535.                      env, pc, "");
  1536.  
  1537.             if(!get_body_part_text(pine_state->mail_stream,
  1538.                        &orig_body->contents.part->body,
  1539.                        mn_m2raw(pine_state->msgmap, msgno),
  1540.                        "1", pc, "", ". Text not included"))
  1541.               goto bomb;
  1542.         } else {
  1543.             q_status_message(SM_ORDER,0,3,
  1544.                      "Multipart with no leading text part!");
  1545.         }
  1546.         } else {
  1547.         /*---- Single non-text message of some sort ----*/
  1548.         q_status_message(SM_ORDER,0,3,
  1549.                  "Non-text message not included!");
  1550.         }
  1551.     }
  1552.     }
  1553.     else {
  1554.     env = mail_fetchstructure(pine_state->mail_stream, msgno, &orig_body);
  1555.     if(!env || !orig_body){
  1556.         q_status_message1(SM_ORDER,3,4,
  1557.                   "Error fetching message %s. Can't forward it.",
  1558.                   long2string(msgno));
  1559.         goto clean_early;
  1560.     }
  1561.  
  1562.         if(orig_body == NULL || orig_body->type == TYPETEXT) {
  1563.             /*---- Message has a single text part -----*/
  1564.             body                  = mail_newbody();
  1565.             body->type            = TYPETEXT;
  1566.             body->contents.binary = msgtext;
  1567.             if(!pine_state->anonymous){
  1568.         forward_delimiter(pc);
  1569.         reply_forward_header(pine_state->mail_stream, msgno, env,
  1570.                      pc, "");
  1571.                      
  1572.         }
  1573.  
  1574.         if(!get_body_part_text(pine_state->mail_stream, orig_body,
  1575.                    msgno, "1", pc, "", ". Text not included"))
  1576.           goto bomb;
  1577. /* BUG: ? matter that we're not setting body.size.bytes */
  1578.         } else if(orig_body->type == TYPEMULTIPART) {
  1579.             /*---- Message is multipart ----*/
  1580.     
  1581.             /*--- Copy the body and entire structure  ---*/
  1582.             body = copy_body(NULL, orig_body);
  1583.     
  1584.             /*--- The text part of the message ---*/
  1585.         if(!orig_body->contents.part){
  1586.         q_status_message(SM_ORDER | SM_DING, 3, 6,
  1587.                  "Error referencing body part 1");
  1588.         goto bomb;
  1589.         }
  1590.         else if(orig_body->contents.part->body.type == TYPETEXT) {
  1591.                 /*--- The first part is text ----*/
  1592.         text_body                  = &body->contents.part->body;
  1593.         text_body->contents.binary = msgtext;
  1594.             if(!pine_state->anonymous){
  1595.             forward_delimiter(pc);
  1596.             reply_forward_header(pine_state->mail_stream, msgno, env,
  1597.                      pc, "");
  1598.         }
  1599.  
  1600.         if(!get_body_part_text(pine_state->mail_stream, 
  1601.                        &orig_body->contents.part->body,
  1602.                        msgno, "1", pc, "",
  1603.                        ". Text not included"))
  1604.           goto bomb;
  1605. /* BUG: ? matter that we're not setting body.size.bytes */
  1606.                 if(!fetch_contents(stream, msgno, body, body))
  1607.                   goto bomb;
  1608.         } else {
  1609.         if(!fetch_contents(stream, msgno, body, body))
  1610.           goto bomb;
  1611.  
  1612.                 /*--- Create a new blank text part ---*/
  1613.                 part                       = mail_newbody_part();
  1614.                 part->next                 = body->contents.part;
  1615.                 body->contents.part        = part;
  1616.                 part->body.contents.binary = msgtext;
  1617.             }
  1618.         } else {
  1619.             /*---- A single part message, not of type text ----*/
  1620.             body                     = mail_newbody();
  1621.             body->type               = TYPEMULTIPART;
  1622.             part                     = mail_newbody_part();
  1623.             body->contents.part      = part;
  1624.     
  1625.             /*--- The first part, a blank text part to be edited ---*/
  1626.             part->body.type            = TYPETEXT;
  1627.             part->body.contents.binary = msgtext;
  1628.     
  1629.             /*--- The second part, what ever it is ---*/
  1630.             part->next               = mail_newbody_part();
  1631.             part                     = part->next;
  1632.             part->body.id            = generate_message_id(pine_state);
  1633.             copy_body(&(part->body), orig_body);
  1634.         /*
  1635.          * the idea here is to fetch part into storage object
  1636.          */
  1637.         if(part->body.contents.binary = (void *) so_get(PART_SO_TYPE, NULL,
  1638.                                 EDIT_ACCESS)){
  1639. #if    defined(DOS) && !defined(WIN32)
  1640.         /* fetched text to disk */
  1641.         mail_parameters(stream, SET_GETS, (void *)dos_gets);
  1642.         append_file = (FILE *)so_text(
  1643.                        (STORE_S *)part->body.contents.binary);
  1644.  
  1645.         if(mail_fetchbody(stream, msgno, "1",
  1646.                     &part->body.size.bytes) == NULL)
  1647.           goto bomb;
  1648.  
  1649.         /* next time body may stay in core */
  1650.         mail_parameters(stream, SET_GETS, (void *)NULL);
  1651.         append_file = NULL;
  1652.         mail_gc(stream, GC_TEXTS);
  1653. #else
  1654.         if(tmp_text = mail_fetchbody(stream, msgno, "1",
  1655.                       &part->body.size.bytes))
  1656.           so_puts((STORE_S *)part->body.contents.binary, tmp_text);
  1657.         else
  1658.           goto bomb;
  1659. #endif
  1660.         }
  1661.         else
  1662.           goto bomb;
  1663.         }
  1664.     }
  1665.  
  1666. #if    defined(DOS) && !defined(_WINDOWS)
  1667.     free((void *)reserve);
  1668. #endif
  1669.     if(pine_state->anonymous)
  1670.       pine_simple_send(outgoing, &body, NULL, NULL, NULL, 1);
  1671.     else            /* partially formatted outgoing message */
  1672.       pine_send(outgoing, &body,
  1673.         pine_state->nr_mode ? "SEND MESSAGE" : "FORWARD MESSAGE",
  1674.         NULL, NULL, NULL, NULL, NULL, NULL, 0);
  1675.  
  1676.   clean:
  1677.     pine_free_body(&body);
  1678.   clean_early:
  1679.     mail_free_envelope(&outgoing);
  1680.     return;
  1681.  
  1682.   bomb:
  1683. #if    defined(DOS) && !defined(WIN32)
  1684.     mail_parameters(stream, SET_GETS, (void *)NULL);
  1685.     append_file = NULL;
  1686.     mail_gc(pine_state->mail_stream, GC_TEXTS);
  1687. #endif
  1688.     q_status_message(SM_ORDER | SM_DING, 4, 5,
  1689.            "Error fetching message contents.  Can't forward message.");
  1690.     goto clean;
  1691. }
  1692.  
  1693.  
  1694.  
  1695. /*----------------------------------------------------------------------
  1696.   Build the subject for the message number being forwarded
  1697.  
  1698.     Args: pine_state -- The usual pine structure
  1699.           msgno      -- The message number to build subject for
  1700.  
  1701.   Result: malloc'd string containing new subject or NULL on error
  1702.  
  1703.   ----------------------------------------------------------------------*/
  1704. char *
  1705. forward_subject(pine_state, msgno)
  1706.      struct pine *pine_state;
  1707.      long      msgno;
  1708. {
  1709.     ENVELOPE *env;
  1710.     size_t    l;
  1711.  
  1712.     if(!(env = mail_fetchstructure(pine_state->mail_stream, msgno, NULL)))
  1713.       return(NULL);
  1714.  
  1715.     dprint(9, (debugfile, "checking subject: \"%s\"\n",
  1716.            env->subject ? env->subject : "NULL"));
  1717.  
  1718.     if(env->subject && env->subject[0]){        /* add (fwd)? */
  1719.     /* decode any 8bit (copy to the temp buffer if decoding doesn't) */
  1720.     if(rfc1522_decode((unsigned char *) tmp_20k_buf,
  1721.              env->subject, NULL) == (unsigned char *) env->subject)
  1722.       strcpy(tmp_20k_buf, env->subject);
  1723.  
  1724.     removing_trailing_white_space(tmp_20k_buf);
  1725.     if((l=strlen(tmp_20k_buf)) < 5 || strcmp(tmp_20k_buf+l-5,"(fwd)"))
  1726. #ifdef    OLDWAY
  1727.       strcat(tmp_20k_buf, " (fwd)");
  1728. #else
  1729.       sprintf(tmp_20k_buf, "%s (fwd)", env->subject);
  1730. #endif
  1731.     return(cpystr(tmp_20k_buf));
  1732.  
  1733.     }
  1734.  
  1735.     return(cpystr("Forwarded mail...."));
  1736. }
  1737.  
  1738.  
  1739.  
  1740. /*----------------------------------------------------------------------
  1741.        Partially set up message to forward and pass off to composer/mailer
  1742.  
  1743.     Args: pine_state -- The usual pine structure
  1744.           message    -- The MESSAGECACHE of entry to reply to 
  1745.  
  1746.   Result: outgoing envelope and body created and passed off to composer/mailer
  1747.  
  1748.    Create the outgoing envelope for the mail being forwarded, which is 
  1749. not much more than filling in the subject, and create the message body
  1750. of the outgoing message which requires formatting the header from the
  1751. envelope of the original messasge.
  1752.   ----------------------------------------------------------------------*/
  1753. void
  1754. forward_text(pine_state, text, source)
  1755.      struct pine *pine_state;
  1756.      void        *text;
  1757.      SourceType   source;
  1758. {
  1759.     ENVELOPE *env;
  1760.     BODY     *body;
  1761.     gf_io_t   pc, gc;
  1762.     STORE_S  *msgtext;
  1763.     char     *enc_error, *sig;
  1764.  
  1765.     if(msgtext = so_get(PicoText, NULL, EDIT_ACCESS)){
  1766.     env                   = mail_newenvelope();
  1767.     env->message_id       = generate_message_id(pine_state);
  1768.     body                  = mail_newbody();
  1769.     body->type            = TYPETEXT;
  1770.     body->contents.binary = (void *) msgtext;
  1771.  
  1772.     if(!pine_state->anonymous){
  1773.         so_puts(msgtext, *(sig = get_signature()) ? sig : NEWLINE);
  1774.         so_puts(msgtext, NEWLINE);
  1775.         so_puts(msgtext, "----- Included text -----");
  1776.         so_puts(msgtext, NEWLINE);
  1777.         fs_give((void **)&sig);
  1778.     }
  1779.  
  1780.     gf_filter_init();
  1781.     gf_set_so_writec(&pc, msgtext);
  1782.     gf_set_readc(&gc,text,(source == CharStar) ? strlen((char *)text) : 0L,
  1783.              source);
  1784.  
  1785.     if((enc_error = gf_pipe(gc, pc)) == NULL){
  1786.         if(pine_state->anonymous){
  1787.         pine_simple_send(env, &body, NULL, NULL, NULL, 1);
  1788.         pine_state->mangled_footer = 1;
  1789.         }
  1790.         else{
  1791.         pine_send(env, &body, "SEND MESSAGE", NULL, NULL, NULL, NULL,
  1792.               NULL, NULL, 0);
  1793.         pine_state->mangled_screen = 1;
  1794.         }
  1795.     }
  1796.     else{
  1797.         q_status_message1(SM_ORDER | SM_DING, 3, 5,
  1798.                   "Error reading text \"%s\"",enc_error);
  1799.         display_message('x');
  1800.     }
  1801.  
  1802.     mail_free_envelope(&env);
  1803.     pine_free_body(&body);
  1804.     }
  1805.     else {
  1806.     q_status_message(SM_ORDER | SM_DING, 3, 4,
  1807.              "Error allocating message text");
  1808.     display_message('x');
  1809.     }
  1810. }
  1811.  
  1812.  
  1813.  
  1814. /*----------------------------------------------------------------------
  1815.        Partially set up message to resend and pass off to mailer
  1816.  
  1817.     Args: pine_state -- The usual pine structure
  1818.  
  1819.   Result: outgoing envelope and body created and passed off to mailer
  1820.  
  1821.    Create the outgoing envelope for the mail being resent, which is 
  1822. not much more than filling in the subject, and create the message body
  1823. of the outgoing message which requires formatting the header from the
  1824. envelope of the original messasge.
  1825.   ----------------------------------------------------------------------*/
  1826. void
  1827. bounce(pine_state)
  1828.     struct pine   *pine_state;
  1829. {
  1830.     ENVELOPE      *env, *outgoing = NULL;
  1831.     BODY          *body;
  1832.     long           msgno, rawmsgno, firstone;
  1833.     MAILSTREAM    *stream;
  1834.     void          *msgtext;
  1835.     gf_io_t        pc;
  1836.     char          *save_to = NULL, *h, *p, **save_toptr,
  1837.           *prmpt_who, bprmpt_who[80],
  1838.           *prmpt_cnf, bprmpt_cnf[80];
  1839.     int            i, got_body;
  1840.  
  1841.     dprint(4, (debugfile, "\n - bounce -\n"));
  1842.  
  1843.     stream = pine_state->mail_stream;
  1844.     firstone = mn_first_cur(pine_state->msgmap);
  1845.     if(mn_total_cur(pine_state->msgmap) > 1L){
  1846.     sprintf(bprmpt_who, "BOUNCE (redirect) %d messages to : ",
  1847.             mn_total_cur(pine_state->msgmap));
  1848.     prmpt_who = bprmpt_who;
  1849.     sprintf(bprmpt_cnf, "Send %d messages to ",
  1850.             mn_total_cur(pine_state->msgmap));
  1851.     prmpt_cnf = bprmpt_cnf;
  1852.     }
  1853.     else{
  1854.     prmpt_who = NULL;
  1855.     prmpt_cnf = NULL;
  1856.     }
  1857.  
  1858.     for(msgno = firstone; msgno > 0L; msgno = mn_next_cur(pine_state->msgmap)){
  1859.  
  1860.     rawmsgno = mn_m2raw(pine_state->msgmap, msgno);
  1861.     env = mail_fetchstructure(stream, rawmsgno, &body);
  1862.     if(!env || !body){
  1863.         q_status_message(SM_ORDER | SM_DING, 4, 7,
  1864.              "Error fetching message contents. Can't resend message");
  1865.         goto bomb;
  1866.     }
  1867.  
  1868.     outgoing              = mail_newenvelope();
  1869.     outgoing->message_id  = generate_message_id(pine_state);
  1870.  
  1871.     /*==================== Subject ====================*/
  1872.     if(env->subject == NULL || strlen(env->subject) == 0)
  1873.       /* --- Blank, make up one ---*/
  1874.       outgoing->subject = cpystr("Resent mail....");
  1875.  
  1876.     /* build remail'd header */
  1877.     if(h = mail_fetchheader(stream, rawmsgno)){
  1878.         for(p = h, i = 0; p = strchr(p, ':'); p++)
  1879.           i++;
  1880.  
  1881.         /* allocate it */
  1882.         outgoing->remail = (char *) fs_get(strlen(h) + (2 * i) + 1);
  1883.  
  1884.         /*
  1885.          * copy it, "X-"ing out transport headers bothersome to
  1886.          * software but potentially useful to the human recipient...
  1887.          */
  1888.         p = outgoing->remail;
  1889.         bounce_mask_header(&p, h);
  1890.         do
  1891.           if(*h == '\015' && *(h+1) == '\012'){
  1892.           *p++ = *h++;        /* copy CR LF */
  1893.           *p++ = *h++;
  1894.           bounce_mask_header(&p, h);
  1895.           }
  1896.         while(*p++ = *h++);
  1897.     }
  1898.     /* BUG: else complain? */
  1899.          
  1900.     /*
  1901.      * as with all text bound for the composer, build it in 
  1902.      * a storage object of the type it understands...
  1903.      */
  1904.     if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
  1905.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  1906.                  "Error allocating message text");
  1907.         mail_free_envelope(&outgoing);
  1908.         return;
  1909.     }
  1910.  
  1911.     /*
  1912.      * Build a fake body description.  It's ignored by pine_rfc822_header,
  1913.      * but we need to set it to something that makes set_mime_types
  1914.      * not sniff it and pine_rfc822_output_body not re-encode it.
  1915.      * Setting the encoding to (ENCMAX + 1) will work and shouldn't cause
  1916.      * problems unless something tries to access body_encodings[] using
  1917.      * it without proper precautions.  We don't want to use ENCOTHER
  1918.      * cause that tells set_mime_types to sniff it, and we don't want to
  1919.      * use ENC8BIT since that tells pine_rfc822_output_body to qp-encode
  1920.      * it.  When there's time, it'd be nice to clean this interaction
  1921.      * up...
  1922.      */
  1923.     body          = mail_newbody();
  1924.     body->type      = TYPETEXT;
  1925.     body->encoding      = ENCMAX + 1;
  1926.     body->subtype      = cpystr("Plain");
  1927.     body->contents.binary = msgtext;
  1928.     gf_set_so_writec(&pc, (STORE_S *)msgtext);
  1929.  
  1930.     if(mn_total_cur(pine_state->msgmap) > 1L){
  1931.         if(msgno == firstone)
  1932.           save_toptr = &save_to;
  1933.         else{
  1934.         static char *fakedomain = "@";
  1935.         char *tmp_a_string;
  1936.  
  1937.         save_toptr = NULL;
  1938.         /* rfc822_parse_adrlist feels free to destroy input so copy */
  1939.         tmp_a_string = cpystr(save_to);
  1940.         rfc822_parse_adrlist(&outgoing->to, tmp_a_string,
  1941.             (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
  1942.               ? fakedomain : ps_global->maildomain);
  1943.         fs_give((void **)&tmp_a_string);
  1944.         }
  1945.     }
  1946.     else
  1947.       save_toptr = NULL;
  1948.  
  1949.     /* pass NULL body to force mail_fetchtext */
  1950.     if(!(got_body = get_body_part_text(stream, NULL, rawmsgno, "1", pc, "",
  1951.                     ". Text not included"))
  1952.        || pine_simple_send(outgoing, &body, prmpt_who,
  1953.                    prmpt_cnf, save_toptr, msgno==firstone) < 0){
  1954.         pine_free_body(&body);
  1955.         mail_free_envelope(&outgoing);
  1956.         if(!got_body)
  1957.           q_status_message(SM_ORDER | SM_DING, 4, 7,
  1958.              "Error fetching message contents. Can't resend message");
  1959.  
  1960.         goto bomb;
  1961.     }
  1962.  
  1963.     pine_free_body(&body);
  1964.     mail_free_envelope(&outgoing);
  1965.     }
  1966.  
  1967.     if(save_to)
  1968.       fs_give((void **)&save_to);
  1969.  
  1970.     return;
  1971.  
  1972.   bomb:
  1973. #if    defined(DOS) && !defined(WIN32)
  1974.     mail_parameters(stream, SET_GETS, (void *)NULL);
  1975.     append_file = NULL;
  1976.     mail_gc(pine_state->mail_stream, GC_TEXTS);
  1977. #endif
  1978.     if(save_to)
  1979.       fs_give((void **)&save_to);
  1980.  
  1981.     return;
  1982.  
  1983. }
  1984.  
  1985.  
  1986.  
  1987. /*----------------------------------------------------------------------
  1988.     Mask off any header entries we don't want xport software to see
  1989.  
  1990. Args:  d -- destination string pointer pointer
  1991.        s -- source string pointer pointer
  1992.  
  1993.   ----*/
  1994. void
  1995. bounce_mask_header(d, s)
  1996.     char **d, *s;
  1997. {
  1998.     if((*s == 'R' || *s == 'r')
  1999.        && (!struncmp(s+1, "esent-", 6) || !struncmp(s+1, "eceived:", 8))){
  2000.     *(*d)++ = 'X';                /* write mask */
  2001.     *(*d)++ = '-';
  2002.     }
  2003. }
  2004.  
  2005.  
  2006.         
  2007. /*----------------------------------------------------------------------
  2008.     Fetch and format text for forwarding
  2009.  
  2010. Args:  stream      -- Mail stream to fetch text for
  2011.        message_no  -- Message number of text for foward
  2012.        part_number -- Part number of text to forward
  2013.        env         -- Envelope of message being forwarded
  2014.        body        -- Body structure of message being forwarded
  2015.  
  2016. Returns:  true if OK, false if problem occured while filtering
  2017.  
  2018. If the text is richtext, it will be converted to plain text, since there's
  2019. no rich text editing capabilities in Pine (yet). The character sets aren't
  2020. really handled correctly here. Theoretically editing should not be allowed
  2021. if text character set doesn't match what term-character-set is set to.
  2022.  
  2023. It's up to calling routines to plug in signature appropriately
  2024.  
  2025. As with all internal text, NVT end-of-line conventions are observed.
  2026. DOESN'T sanity check the prefix given!!!
  2027.   ----*/
  2028. int
  2029. get_body_part_text(stream, body, msg_no, part_no, pc, prefix, hmm)
  2030.     MAILSTREAM *stream;
  2031.     BODY       *body;
  2032.     long        msg_no;
  2033.     char       *part_no;
  2034.     gf_io_t     pc;
  2035.     char       *prefix;
  2036.     char       *hmm; 
  2037. {
  2038.     int          i, we_cancel = 0;
  2039.     filter_t  filters[3];
  2040.     long      len;
  2041.     char     *err;
  2042. #if    defined(DOS) && !defined(WIN32)
  2043.     char     *tmpfile_name = NULL;
  2044. #endif
  2045.  
  2046.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  2047.  
  2048.     /* if null body, we must be talking to a non-IMAP2bis server.
  2049.      * No MIME parsing provided, so we just grab the message text...
  2050.      */
  2051.     if(body == NULL){
  2052.     char         *text, *decode_error;
  2053.     MESSAGECACHE *mc;
  2054.     gf_io_t       gc;
  2055.     SourceType    src = CharStar;
  2056.     int           rv = 0;
  2057.  
  2058.     (void)mail_fetchstructure(stream, msg_no, NULL);
  2059.     mc = mail_elt(stream,  msg_no);
  2060.  
  2061. #if    defined(DOS) && !defined(WIN32)
  2062.     if(mc->rfc822_size > MAX_MSG_INCORE
  2063.       || (ps_global->context_current->type & FTYPE_BBOARD)){
  2064.         src = FileStar;        /* write fetched text to disk */
  2065.         if(!(tmpfile_name = temp_nam(NULL, "pt"))
  2066.            || !(append_file = fopen(tmpfile_name, "w+b"))){
  2067.         if(tmpfile_name)
  2068.           fs_give((void **)&tmpfile_name);
  2069.  
  2070.         q_status_message1(SM_ORDER,3,4,"Can't build tmpfile: %s",
  2071.                   error_description(errno));
  2072.         if(we_cancel)
  2073.           cancel_busy_alarm(-1);
  2074.  
  2075.         return(rv);
  2076.         }
  2077.  
  2078.         mail_parameters(stream, SET_GETS, (void *)dos_gets);
  2079.     }
  2080.     else                /* message stays in core */
  2081.       mail_parameters(stream, SET_GETS, (void *)NULL);
  2082. #endif
  2083.  
  2084.     if(text = mail_fetchtext(stream, msg_no)){
  2085. #if    defined(DOS) && !defined(WIN32)
  2086.         if(src == FileStar)
  2087.           gf_set_readc(&gc, append_file, 0L, src);
  2088.         else
  2089. #endif
  2090.         gf_set_readc(&gc, text, (unsigned long)strlen(text), src);
  2091.  
  2092.         gf_filter_init();        /* no filters needed */
  2093.         if(decode_error = gf_pipe(gc, pc)){
  2094.         sprintf(tmp_20k_buf, "%s%s    [Formatting error: %s]%s",
  2095.             NEWLINE, NEWLINE,
  2096.             decode_error, NEWLINE);
  2097.         gf_puts(tmp_20k_buf, pc);
  2098.         rv++;
  2099.         }
  2100.     }
  2101.     else{
  2102.         gf_puts(NEWLINE, pc);
  2103.         gf_puts("    [ERROR fetching text of message]", pc);
  2104.         gf_puts(NEWLINE, pc);
  2105.         gf_puts(NEWLINE, pc);
  2106.         rv++;
  2107.     }
  2108.  
  2109. #if    defined(DOS) && !defined(WIN32)
  2110.     /* clean up tmp file created for dos_gets ?  If so, trash
  2111.      * cached knowledge, and make sure next fetch stays in core
  2112.      */
  2113.     if(src == FileStar){
  2114.         fclose(append_file);
  2115.         append_file = NULL;
  2116.         unlink(tmpfile_name);
  2117.         fs_give((void **)&tmpfile_name);
  2118.         mail_parameters(stream, SET_GETS, (void *)NULL);
  2119.         mail_gc(stream, GC_TEXTS);
  2120.     }
  2121. #endif
  2122.     if(we_cancel)
  2123.       cancel_busy_alarm(-1);
  2124.  
  2125.     return(rv == 0);
  2126.     }
  2127.  
  2128.     filters[i = 0] = NULL;
  2129.  
  2130.     /*
  2131.      * just use detach, but add an auxiliary filter to insert prefix,
  2132.      * and, perhaps, digest richtext
  2133.      */
  2134.     if(body->subtype != NULL && strucmp(body->subtype,"richtext") == 0){
  2135.     gf_rich2plain_opt(1);        /* set up to filter richtext */
  2136.     filters[i++] = gf_rich2plain;
  2137.     }
  2138.  
  2139. /* BUG: is msgno stuff working right ??? */
  2140. /* BUG: not using "hmm" !!! */
  2141.     gf_prefix_opt(prefix);
  2142.     filters[i++] = gf_prefix;
  2143.     filters[i++] = NULL;
  2144.     err = detach(stream, msg_no, body, part_no, &len, pc, filters);
  2145.     if (err != (char *)NULL)
  2146.        q_status_message2(SM_ORDER, 3, 4,
  2147.        "%s: message number %ld",err,(void *)msg_no);
  2148.     if(we_cancel)
  2149.       cancel_busy_alarm(-1);
  2150.  
  2151.     return((int)len);
  2152. }
  2153.  
  2154.  
  2155.  
  2156. /*----------------------------------------------------------------------
  2157.   return the c-client reference name for the given end_body part
  2158.   ----*/
  2159. char *
  2160. partno(body, end_body)
  2161.      BODY *body, *end_body;
  2162. {
  2163.     PART *part;
  2164.     int   num = 0;
  2165.     char  tmp[64], *p = NULL;
  2166.  
  2167.     if(body && body->type == TYPEMULTIPART) {
  2168.     part = body->contents.part;    /* first body part */
  2169.  
  2170.     do {                /* for each part */
  2171.         num++;
  2172.         if(&part->body == end_body || (p = partno(&part->body, end_body))){
  2173.         sprintf(tmp, "%d%s%s", num, (p) ? "." : "", (p) ? p : "");
  2174.         if(p)
  2175.           fs_give((void **)&p);
  2176.  
  2177.         return(cpystr(tmp));
  2178.         }
  2179.     } while (part = part->next);    /* until done */
  2180.  
  2181.     return(NULL);
  2182.     }
  2183.     else if(body && body->type == TYPEMESSAGE && body->subtype 
  2184.         && !strucmp(body->subtype, "rfc822")){
  2185.     return(partno(body->contents.msg.body, end_body));
  2186.     }
  2187.  
  2188.     return((body == end_body) ? cpystr("1") : NULL);
  2189. }
  2190.  
  2191.  
  2192.  
  2193. /*----------------------------------------------------------------------
  2194.    Fill in the contents of each body part
  2195.  
  2196. Args: stream      -- Stream the message is on
  2197.       msgno       -- Message number the body structure is for
  2198.       root        -- Body pointer to start from
  2199.       body        -- Body pointer to fill in
  2200.  
  2201. Result: 1 if all went OK, 0 if there was a problem
  2202.  
  2203. This function copies the contents from an original message/body to
  2204. a new message/body.  It recurses down all multipart levels.
  2205.  
  2206. If one or more part (but not all) can't be fetched, a status message
  2207. will be queued.
  2208.  ----*/
  2209. int
  2210. fetch_contents(stream, msgno, root, body)
  2211.      MAILSTREAM *stream;
  2212.      long        msgno;
  2213.      BODY       *root, *body;
  2214. {
  2215.     char *pnum = NULL, *tp;
  2216.     int   got_one = 0;
  2217.  
  2218.     if(!body->id)
  2219.       body->id = generate_message_id(ps_global);
  2220.           
  2221.     if(body->type == TYPEMULTIPART){
  2222.     int   last_one = 10;        /* remember worst case */
  2223.     PART *part     = body->contents.part;
  2224.  
  2225.     if(!(part = body->contents.part))
  2226.       return(0);
  2227.  
  2228.     do {
  2229.         got_one  = fetch_contents(stream, msgno, root, &part->body);
  2230.         last_one = min(last_one, got_one);
  2231.     }
  2232.     while(part = part->next);
  2233.  
  2234.     return(last_one);
  2235.     }
  2236.  
  2237.     if(body->contents.binary)
  2238.       return(1);            /* already taken care of... */
  2239.  
  2240.     pnum = partno(root, body);
  2241.  
  2242.     if(body->type == TYPEMESSAGE){
  2243.     body->contents.msg.env  = NULL;
  2244.     body->contents.msg.body = NULL;
  2245.     if(body->subtype && strucmp(body->subtype,"external-body")){
  2246.         /*
  2247.          * the idea here is to fetch everything into storage objects
  2248.          */
  2249.         body->contents.binary = (void *) so_get(PART_SO_TYPE, NULL,
  2250.                             EDIT_ACCESS);
  2251. #if    defined(DOS) && !defined(WIN32)
  2252.         if(body->contents.binary){
  2253.         /* fetch text to disk */
  2254.         mail_parameters(stream, SET_GETS, (void *)dos_gets);
  2255.         append_file =(FILE *)so_text((STORE_S *)body->contents.binary);
  2256.  
  2257.         if(mail_fetchbody(stream, msgno, pnum, &body->size.bytes)){
  2258.             so_release((STORE_S *)body->contents.binary);
  2259.             got_one = 1;
  2260.         }
  2261.         else
  2262.           q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2263.                     "Error fetching part %s",pnum);
  2264.  
  2265.         /* next time body may stay in core */
  2266.         mail_parameters(stream, SET_GETS, (void *)NULL);
  2267.         append_file = NULL;
  2268.         mail_gc(stream, GC_TEXTS);
  2269.         }
  2270. #else
  2271.         if(body->contents.binary
  2272.            && (tp = mail_fetchbody(stream,msgno,pnum,&body->size.bytes))){
  2273.         so_puts((STORE_S *)body->contents.binary, tp);
  2274.         got_one = 1;
  2275.         }
  2276. #endif
  2277.         else
  2278.           q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2279.                 "Error fetching part %s",pnum);
  2280.     } else {
  2281.         got_one = 1;
  2282.     }
  2283.     } else {
  2284.     /*
  2285.      * the idea here is to fetch everything into storage objects
  2286.      * so, grab one, then fetch the body part
  2287.      */
  2288.     body->contents.binary = (void *)so_get(PART_SO_TYPE,NULL,EDIT_ACCESS);
  2289. #if    defined(DOS) && !defined(WIN32)
  2290.     if(body->contents.binary){
  2291.         /* write fetched text to disk */
  2292.         mail_parameters(stream, SET_GETS, (void *)dos_gets);
  2293.         append_file = (FILE *)so_text((STORE_S *)body->contents.binary);
  2294.         if(mail_fetchbody(stream, msgno, pnum, &body->size.bytes)){
  2295.         so_release((STORE_S *)body->contents.binary);
  2296.         got_one = 1;
  2297.         }
  2298.         else
  2299.           q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2300.                 "Error fetching part %s",pnum);
  2301.  
  2302.         /* next time body may stay in core */
  2303.         mail_parameters(stream, SET_GETS, (void *)NULL);
  2304.         append_file = NULL;
  2305.         mail_gc(stream, GC_TEXTS);
  2306.     }
  2307. #else
  2308.     if(body->contents.binary
  2309.        && (tp=mail_fetchbody(stream, msgno, pnum, &body->size.bytes))){
  2310.         so_puts((STORE_S *)body->contents.binary, tp);
  2311.         got_one = 1;
  2312.     }
  2313. #endif
  2314.     else
  2315.       q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2316.                 "Error fetching part %s",pnum);
  2317.     }
  2318.  
  2319.     if(pnum)
  2320.       fs_give((void **)&pnum);
  2321.  
  2322.     return(got_one);
  2323.  
  2324. }
  2325.  
  2326.  
  2327.  
  2328. /*----------------------------------------------------------------------
  2329.     Copy the body structure
  2330.  
  2331. Args: new_body -- Pointer to already allocated body, or NULL, if none
  2332.       old_body -- The Body to copy
  2333.  
  2334.  
  2335.  This is called recursively traverses the body structure copying all the
  2336. elements. The new_body parameter can be NULL in which case a new body is
  2337. allocated. Alternatively it can point to an already allocated body
  2338. structure. This is used when copying body parts since a PART includes a 
  2339. BODY. The contents fields are *not* filled in.
  2340.   ----*/
  2341.  
  2342. BODY *
  2343. copy_body(new_body, old_body)
  2344.      BODY *old_body, *new_body;
  2345. {
  2346.     PART *new_part, *old_part;
  2347.  
  2348.     if(old_body == NULL)
  2349.       return(NULL);
  2350.  
  2351.     if(new_body == NULL)
  2352.       new_body = mail_newbody();
  2353.  
  2354.     *new_body = *old_body;
  2355.     if(old_body->id)
  2356.       new_body->id = cpystr(old_body->id);
  2357.  
  2358.     if(old_body->description)
  2359.       new_body->description = cpystr(old_body->description);
  2360.  
  2361.     if(old_body->subtype)
  2362.       new_body->subtype = cpystr(old_body->subtype);
  2363.  
  2364.     new_body->parameter = copy_parameters(old_body->parameter);
  2365.  
  2366.     new_part = NULL;
  2367.     if(new_body->type == TYPEMULTIPART) {
  2368.         for(old_part = new_body->contents.part; old_part != NULL;
  2369.             old_part = old_part->next){
  2370.             if(new_part == NULL) {
  2371.                 new_part = mail_newbody_part();
  2372.                 new_body->contents.part = new_part;
  2373.             } else {
  2374.                 new_part->next = mail_newbody_part();
  2375.                 new_part = new_part->next;
  2376.             }
  2377.             copy_body(&(new_part->body), &(old_part->body));
  2378.         }
  2379.     } else {
  2380.         new_body->contents.binary = NULL;
  2381.     }
  2382.     return(new_body);
  2383. }
  2384.  
  2385.  
  2386.  
  2387. /*----------------------------------------------------------------------
  2388.     Copy the MIME parameter list
  2389.  
  2390.  Allocates storage for new part, and returns pointer to new paramter
  2391. list. If old_p is NULL, NULL is returned.
  2392.  ----*/
  2393.  
  2394. PARAMETER *
  2395. copy_parameters(old_p)
  2396.      PARAMETER *old_p;
  2397. {
  2398.     PARAMETER *new_p, *p1, *p2;
  2399.  
  2400.     if(old_p == NULL)
  2401.       return((PARAMETER *)NULL);
  2402.  
  2403.     new_p = p2 = NULL;
  2404.     for(p1 = old_p; p1 != NULL; p1 = p1->next) {
  2405.         if(new_p == NULL) {
  2406.             p2 = mail_newbody_parameter();
  2407.             new_p = p2;
  2408.         } else {
  2409.             p2->next = mail_newbody_parameter();
  2410.             p2 = p2->next;
  2411.         }
  2412.         p2->attribute = cpystr(p1->attribute);
  2413.         p2->value     = cpystr(p1->value);
  2414.     }
  2415.     return(new_p);
  2416. }
  2417.     
  2418.     
  2419.  
  2420. /*----------------------------------------------------------------------
  2421.     Make a complete copy of an envelope and all it's fields
  2422.  
  2423. Args:    e -- the envelope to copy
  2424.  
  2425. Result:  returns the new envelope, or NULL, if the given envelope was NULL
  2426.  
  2427.   ----*/
  2428.  
  2429. ENVELOPE *    
  2430. copy_envelope(e)
  2431.      register ENVELOPE *e;
  2432. {
  2433.     register ENVELOPE *e2;
  2434.  
  2435.     if(!e)
  2436.       return(NULL);
  2437.  
  2438.     e2            = mail_newenvelope();
  2439.     e2->remail      = e->remail         ? cpystr(e->remail)          : NULL;
  2440.     e2->return_path = e->return_path ? rfc822_cpy_adr(e->return_path) : NULL;
  2441.     e2->date        = e->date         ? cpystr(e->date)              : NULL;
  2442.     e2->from        = e->from         ? rfc822_cpy_adr(e->from)          : NULL;
  2443.     e2->sender      = e->sender         ? rfc822_cpy_adr(e->sender)      : NULL;
  2444.     e2->reply_to    = e->reply_to    ? rfc822_cpy_adr(e->reply_to)    : NULL;
  2445.     e2->subject     = e->subject     ? cpystr(e->subject)          : NULL;
  2446.     e2->to          = e->to          ? rfc822_cpy_adr(e->to)          : NULL;
  2447.     e2->cc          = e->cc          ? rfc822_cpy_adr(e->cc)          : NULL;
  2448.     e2->bcc         = e->bcc         ? rfc822_cpy_adr(e->bcc)          : NULL;
  2449.     e2->in_reply_to = e->in_reply_to ? cpystr(e->in_reply_to)          : NULL;
  2450.     e2->newsgroups  = e->newsgroups  ? cpystr(e->newsgroups)          : NULL;
  2451.     e2->message_id  = e->message_id  ? cpystr(e->message_id)          : NULL;
  2452.     return(e2);
  2453. }
  2454.  
  2455.  
  2456. /*----------------------------------------------------------------------
  2457.      Generate the "In-reply-to" text from message header
  2458.  
  2459.   Args: message -- Envelope of original message
  2460.  
  2461.   Result: returns an alloc'd string or NULL if there is a problem
  2462.  ----*/
  2463. char *
  2464. generate_in_reply_to(env)
  2465.     ENVELOPE *env;
  2466. {
  2467.     return((env && env->message_id) ? cpystr(env->message_id) : NULL);
  2468. }
  2469.  
  2470.  
  2471. /*----------------------------------------------------------------------
  2472.         Generate a unique message id string.
  2473.  
  2474.    Args: ps -- The usual pine structure
  2475.  
  2476.   Result: Alloc'd unique string is returned
  2477.  
  2478. Uniqueness is gaurenteed by using the host name, process id, date to the
  2479. second and a single unique character
  2480. *----------------------------------------------------------------------*/
  2481. char *
  2482. generate_message_id(ps)
  2483.      struct pine *ps;
  2484. {
  2485.     static char a = 'A';
  2486.     char       *id;
  2487.     time_t      now;
  2488.     struct tm  *now_x;
  2489.  
  2490.     now   = time((time_t *)0);
  2491.     now_x = localtime(&now);
  2492.     id    = (char *)fs_get(128 * sizeof(char));
  2493.  
  2494.     sprintf(id,"<Pine.%.4s.%.20s.%02d%02d%02d%02d%02d%02d.%d%c@%.50s>",
  2495.         SYSTYPE, pine_version, now_x->tm_year, now_x->tm_mon + 1,
  2496.         now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec,
  2497.         getpid(), a, ps->hostname);
  2498.  
  2499.     a = (a == 'Z') ? 'a' : (a == 'z') ? 'A' : a + 1;
  2500.     return(id);
  2501. }
  2502.  
  2503.  
  2504.  
  2505. /*----------------------------------------------------------------------
  2506.   Return the first true address pointer (modulo group syntax allowance)
  2507.  
  2508.   Args: addr  -- Address list
  2509.  
  2510.  Result: First real address pointer, or NULL
  2511.   ----------------------------------------------------------------------*/
  2512. ADDRESS *
  2513. first_addr(addr)
  2514.     ADDRESS *addr;
  2515. {
  2516.     while(addr && !addr->host)
  2517.       addr = addr->next;
  2518.  
  2519.     return(addr);
  2520. }
  2521.  
  2522.  
  2523.  
  2524. /*----------------------------------------------------------------------
  2525.   Acquire the pinerc defined signature file
  2526.  
  2527.   ----*/
  2528. char *
  2529. get_signature()
  2530. {
  2531.     char *sig = NULL, *tmp_sig, sig_path[MAXPATH+1];
  2532.  
  2533.     /*----- Get the signature if there is one to get -----*/
  2534.     if(signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH)
  2535.        && can_access(sig_path, ACCESS_EXISTS) == 0){
  2536.     if(tmp_sig = read_file(sig_path)){
  2537.         sig = fs_get(strlen(tmp_sig) + 10);
  2538.         strcpy(sig, NEWLINE);
  2539.         strcat(sig, NEWLINE);
  2540.         strcat(sig, tmp_sig);
  2541.         fs_give((void **)&tmp_sig);
  2542.     }
  2543.     else
  2544.       q_status_message2(SM_ORDER | SM_DING, 3, 4,
  2545.                 "Error \"%s\" reading signature file \"%s\"",
  2546.                 error_description(errno), sig_path);
  2547.     }
  2548.  
  2549.     return(sig ? sig : cpystr(""));
  2550. }
  2551.  
  2552.  
  2553.  
  2554. /*----------------------------------------------------------------------
  2555.   Acquire the pinerc defined signature file pathname
  2556.  
  2557.   ----*/
  2558. char *
  2559. signature_path(sname, sbuf, len)
  2560.     char   *sname, *sbuf;
  2561.     size_t  len;
  2562. {
  2563.     *sbuf = '\0';
  2564.     if(sname && *sname){
  2565.     size_t spl = strlen(sname);
  2566.     if(is_absolute_path(sname)){
  2567.         if(spl < len - 1)
  2568.           strcpy(sbuf, sname);
  2569.     }
  2570. #ifndef    DOS
  2571.     else if(ps_global->VAR_SIGNATURE_FILE[0] == '~'){
  2572.         strcpy(sbuf, sname);
  2573.         fnexpand(sbuf, len);
  2574.     }
  2575. #endif
  2576.     else{
  2577.         char *lc = last_cmpnt(ps_global->pinerc);
  2578.  
  2579.         sbuf[0] = '\0';
  2580.         if(lc != NULL){
  2581.         strncpy(sbuf,ps_global->pinerc,min(len-1,lc-ps_global->pinerc));
  2582.         sbuf[min(len-1,lc-ps_global->pinerc)] = '\0';
  2583.         }
  2584.  
  2585.         sbuf[spl+ strlen(sbuf)] = '\0';
  2586.         strncat(sbuf, sname, spl + strlen(sbuf));
  2587.     }
  2588.     }
  2589.  
  2590.     return(*sbuf ? sbuf : NULL);
  2591. }
  2592.